diff --git a/.github/actions/fetch-codeql/action.yml b/.github/actions/fetch-codeql/action.yml
index 13b91525237..02098892663 100644
--- a/.github/actions/fetch-codeql/action.yml
+++ b/.github/actions/fetch-codeql/action.yml
@@ -3,22 +3,12 @@ description: Fetches the latest version of CodeQL
runs:
using: composite
steps:
- - name: Select platform - Linux
- if: runner.os == 'Linux'
- shell: bash
- run: echo "GA_CODEQL_CLI_PLATFORM=linux64" >> $GITHUB_ENV
-
- - name: Select platform - MacOS
- if: runner.os == 'MacOS'
- shell: bash
- run: echo "GA_CODEQL_CLI_PLATFORM=osx64" >> $GITHUB_ENV
-
- name: Fetch CodeQL
shell: bash
run: |
- LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1)
- gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-$GA_CODEQL_CLI_PLATFORM.zip "$LATEST"
- unzip -q -d "${RUNNER_TEMP}" codeql-$GA_CODEQL_CLI_PLATFORM.zip
- echo "${RUNNER_TEMP}/codeql" >> "${GITHUB_PATH}"
+ gh extension install github/gh-codeql
+ gh codeql set-channel nightly
+ gh codeql version
+ gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}"
env:
GITHUB_TOKEN: ${{ github.token }}
diff --git a/.github/workflows/check-change-note.yml b/.github/workflows/check-change-note.yml
index 672202444bb..b60a590ab09 100644
--- a/.github/workflows/check-change-note.yml
+++ b/.github/workflows/check-change-note.yml
@@ -10,6 +10,7 @@ on:
- "*/ql/lib/**/*.qll"
- "!**/experimental/**"
- "!ql/**"
+ - "!swift/**"
- ".github/workflows/check-change-note.yml"
jobs:
diff --git a/.github/workflows/check-qldoc.yml b/.github/workflows/check-qldoc.yml
index 77f524b73e7..cc7523162aa 100644
--- a/.github/workflows/check-qldoc.yml
+++ b/.github/workflows/check-qldoc.yml
@@ -5,6 +5,7 @@ on:
paths:
- "*/ql/lib/**"
- .github/workflows/check-qldoc.yml
+ - .github/actions/fetch-codeql/action.yml
branches:
- main
- "rc/*"
@@ -14,18 +15,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- - name: Install CodeQL
- run: |
- gh extension install github/gh-codeql
- gh codeql set-channel nightly
- gh codeql version
- env:
- GITHUB_TOKEN: ${{ github.token }}
-
- uses: actions/checkout@v3
with:
fetch-depth: 2
+ - name: Install CodeQL
+ uses: ./.github/actions/fetch-codeql
+
- name: Check QLdoc coverage
shell: bash
run: |
@@ -34,7 +30,7 @@ jobs:
changed_lib_packs="$(git diff --name-only --diff-filter=ACMRT HEAD^ HEAD | { grep -Po '^(?!swift)[a-z]*/ql/lib' || true; } | sort -u)"
for pack_dir in ${changed_lib_packs}; do
lang="${pack_dir%/ql/lib}"
- gh codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-current.txt" --dir="${pack_dir}"
+ codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-current.txt" --dir="${pack_dir}"
done
git checkout HEAD^
for pack_dir in ${changed_lib_packs}; do
@@ -42,7 +38,7 @@ jobs:
# In this case the right thing to do is to skip the check.
[[ ! -d "${pack_dir}" ]] && continue
lang="${pack_dir%/ql/lib}"
- gh codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-baseline.txt" --dir="${pack_dir}"
+ codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-baseline.txt" --dir="${pack_dir}"
awk -F, '{gsub(/"/,""); if ($4==0 && $6=="public") print "\""$3"\"" }' "${RUNNER_TEMP}/${lang}-current.txt" | sort -u > "${RUNNER_TEMP}/current-undocumented.txt"
awk -F, '{gsub(/"/,""); if ($4==0 && $6=="public") print "\""$3"\"" }' "${RUNNER_TEMP}/${lang}-baseline.txt" | sort -u > "${RUNNER_TEMP}/baseline-undocumented.txt"
UNDOCUMENTED="$(grep -f <(comm -13 "${RUNNER_TEMP}/baseline-undocumented.txt" "${RUNNER_TEMP}/current-undocumented.txt") "${RUNNER_TEMP}/${lang}-current.txt" || true)"
diff --git a/.github/workflows/csv-coverage-metrics.yml b/.github/workflows/csv-coverage-metrics.yml
index 7778221dc2f..7555533ab98 100644
--- a/.github/workflows/csv-coverage-metrics.yml
+++ b/.github/workflows/csv-coverage-metrics.yml
@@ -12,6 +12,7 @@ on:
- main
paths:
- ".github/workflows/csv-coverage-metrics.yml"
+ - ".github/actions/fetch-codeql/action.yml"
jobs:
publish-java:
diff --git a/.github/workflows/csv-coverage-pr-artifacts.yml b/.github/workflows/csv-coverage-pr-artifacts.yml
index 379b3c5aad8..19ad488a3ab 100644
--- a/.github/workflows/csv-coverage-pr-artifacts.yml
+++ b/.github/workflows/csv-coverage-pr-artifacts.yml
@@ -3,18 +3,20 @@ name: Check framework coverage changes
on:
pull_request:
paths:
- - '.github/workflows/csv-coverage-pr-comment.yml'
- - '*/ql/src/**/*.ql'
- - '*/ql/src/**/*.qll'
- - '*/ql/lib/**/*.ql'
- - '*/ql/lib/**/*.qll'
- - 'misc/scripts/library-coverage/*.py'
+ - ".github/workflows/csv-coverage-pr-comment.yml"
+ - ".github/workflows/csv-coverage-pr-artifacts.yml"
+ - ".github/actions/fetch-codeql/action.yml"
+ - "*/ql/src/**/*.ql"
+ - "*/ql/src/**/*.qll"
+ - "*/ql/lib/**/*.ql"
+ - "*/ql/lib/**/*.qll"
+ - "misc/scripts/library-coverage/*.py"
# input data files
- - '*/documentation/library-coverage/cwe-sink.csv'
- - '*/documentation/library-coverage/frameworks.csv'
+ - "*/documentation/library-coverage/cwe-sink.csv"
+ - "*/documentation/library-coverage/frameworks.csv"
branches:
- main
- - 'rc/*'
+ - "rc/*"
jobs:
generate:
@@ -23,77 +25,72 @@ jobs:
runs-on: ubuntu-latest
steps:
- - name: Dump GitHub context
- env:
- GITHUB_CONTEXT: ${{ toJSON(github.event) }}
- run: echo "$GITHUB_CONTEXT"
- - name: Clone self (github/codeql) - MERGE
- uses: actions/checkout@v3
- with:
- path: merge
- - name: Clone self (github/codeql) - BASE
- uses: actions/checkout@v3
- with:
- fetch-depth: 2
- path: base
- - run: |
- git checkout HEAD^1
- git log -1 --format='%H'
- working-directory: base
- - name: Set up Python 3.8
- uses: actions/setup-python@v4
- with:
- python-version: 3.8
- - name: Download CodeQL CLI
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip"
- - name: Unzip CodeQL CLI
- run: unzip -d codeql-cli codeql-linux64.zip
- - name: Generate CSV files on merge commit of the PR
- run: |
- echo "Running generator on merge"
- PATH="$PATH:codeql-cli/codeql" python merge/misc/scripts/library-coverage/generate-report.py ci merge merge
- mkdir out_merge
- cp framework-coverage-*.csv out_merge/
- cp framework-coverage-*.rst out_merge/
- - name: Generate CSV files on base commit of the PR
- run: |
- echo "Running generator on base"
- PATH="$PATH:codeql-cli/codeql" python base/misc/scripts/library-coverage/generate-report.py ci base base
- mkdir out_base
- cp framework-coverage-*.csv out_base/
- cp framework-coverage-*.rst out_base/
- - name: Generate diff of coverage reports
- run: |
- python base/misc/scripts/library-coverage/compare-folders.py out_base out_merge comparison.md
- - name: Upload CSV package list
- uses: actions/upload-artifact@v3
- with:
- name: csv-framework-coverage-merge
- path: |
- out_merge/framework-coverage-*.csv
- out_merge/framework-coverage-*.rst
- - name: Upload CSV package list
- uses: actions/upload-artifact@v3
- with:
- name: csv-framework-coverage-base
- path: |
- out_base/framework-coverage-*.csv
- out_base/framework-coverage-*.rst
- - name: Upload comparison results
- uses: actions/upload-artifact@v3
- with:
- name: comparison
- path: |
- comparison.md
- - name: Save PR number
- run: |
- mkdir -p pr
- echo ${{ github.event.pull_request.number }} > pr/NR
- - name: Upload PR number
- uses: actions/upload-artifact@v3
- with:
- name: pr
- path: pr/
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJSON(github.event) }}
+ run: echo "$GITHUB_CONTEXT"
+ - name: Clone self (github/codeql) - MERGE
+ uses: actions/checkout@v3
+ with:
+ path: merge
+ - name: Clone self (github/codeql) - BASE
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 2
+ path: base
+ - run: |
+ git checkout HEAD^1
+ git log -1 --format='%H'
+ working-directory: base
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v4
+ with:
+ python-version: 3.8
+ - name: Download CodeQL CLI
+ uses: ./merge/.github/actions/fetch-codeql
+ - name: Generate CSV files on merge commit of the PR
+ run: |
+ echo "Running generator on merge"
+ python merge/misc/scripts/library-coverage/generate-report.py ci merge merge
+ mkdir out_merge
+ cp framework-coverage-*.csv out_merge/
+ cp framework-coverage-*.rst out_merge/
+ - name: Generate CSV files on base commit of the PR
+ run: |
+ echo "Running generator on base"
+ python base/misc/scripts/library-coverage/generate-report.py ci base base
+ mkdir out_base
+ cp framework-coverage-*.csv out_base/
+ cp framework-coverage-*.rst out_base/
+ - name: Generate diff of coverage reports
+ run: |
+ python base/misc/scripts/library-coverage/compare-folders.py out_base out_merge comparison.md
+ - name: Upload CSV package list
+ uses: actions/upload-artifact@v3
+ with:
+ name: csv-framework-coverage-merge
+ path: |
+ out_merge/framework-coverage-*.csv
+ out_merge/framework-coverage-*.rst
+ - name: Upload CSV package list
+ uses: actions/upload-artifact@v3
+ with:
+ name: csv-framework-coverage-base
+ path: |
+ out_base/framework-coverage-*.csv
+ out_base/framework-coverage-*.rst
+ - name: Upload comparison results
+ uses: actions/upload-artifact@v3
+ with:
+ name: comparison
+ path: |
+ comparison.md
+ - name: Save PR number
+ run: |
+ mkdir -p pr
+ echo ${{ github.event.pull_request.number }} > pr/NR
+ - name: Upload PR number
+ uses: actions/upload-artifact@v3
+ with:
+ name: pr
+ path: pr/
diff --git a/.github/workflows/csv-coverage-timeseries.yml b/.github/workflows/csv-coverage-timeseries.yml
index 95b084ea215..42fd4711dac 100644
--- a/.github/workflows/csv-coverage-timeseries.yml
+++ b/.github/workflows/csv-coverage-timeseries.yml
@@ -5,38 +5,29 @@ on:
jobs:
build:
-
runs-on: ubuntu-latest
steps:
- - name: Clone self (github/codeql)
- uses: actions/checkout@v3
- with:
- path: script
- - name: Clone self (github/codeql) for analysis
- uses: actions/checkout@v3
- with:
- path: codeqlModels
- fetch-depth: 0
- - name: Set up Python 3.8
- uses: actions/setup-python@v4
- with:
- python-version: 3.8
- - name: Download CodeQL CLI
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip"
- - name: Unzip CodeQL CLI
- run: unzip -d codeql-cli codeql-linux64.zip
- - name: Build modeled package list
- run: |
- CLI=$(realpath "codeql-cli/codeql")
- echo $CLI
- PATH="$PATH:$CLI" python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels
- - name: Upload timeseries CSV
- uses: actions/upload-artifact@v3
- with:
- name: framework-coverage-timeseries
- path: framework-coverage-timeseries-*.csv
-
+ - name: Clone self (github/codeql)
+ uses: actions/checkout@v3
+ with:
+ path: script
+ - name: Clone self (github/codeql) for analysis
+ uses: actions/checkout@v3
+ with:
+ path: codeqlModels
+ fetch-depth: 0
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v4
+ with:
+ python-version: 3.8
+ - name: Download CodeQL CLI
+ uses: ./script/.github/actions/fetch-codeql
+ - name: Build modeled package list
+ run: |
+ python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels
+ - name: Upload timeseries CSV
+ uses: actions/upload-artifact@v3
+ with:
+ name: framework-coverage-timeseries
+ path: framework-coverage-timeseries-*.csv
diff --git a/.github/workflows/csv-coverage-update.yml b/.github/workflows/csv-coverage-update.yml
index c57056b6de1..6474044c483 100644
--- a/.github/workflows/csv-coverage-update.yml
+++ b/.github/workflows/csv-coverage-update.yml
@@ -12,33 +12,27 @@ jobs:
runs-on: ubuntu-latest
steps:
- - name: Dump GitHub context
- env:
- GITHUB_CONTEXT: ${{ toJSON(github.event) }}
- run: echo "$GITHUB_CONTEXT"
- - name: Clone self (github/codeql)
- uses: actions/checkout@v3
- with:
- path: ql
- fetch-depth: 0
- - name: Set up Python 3.8
- uses: actions/setup-python@v4
- with:
- python-version: 3.8
- - name: Download CodeQL CLI
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip"
- - name: Unzip CodeQL CLI
- run: unzip -d codeql-cli codeql-linux64.zip
+ - name: Dump GitHub context
+ env:
+ GITHUB_CONTEXT: ${{ toJSON(github.event) }}
+ run: echo "$GITHUB_CONTEXT"
+ - name: Clone self (github/codeql)
+ uses: actions/checkout@v3
+ with:
+ path: ql
+ fetch-depth: 0
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v4
+ with:
+ python-version: 3.8
+ - name: Download CodeQL CLI
+ uses: ./ql/.github/actions/fetch-codeql
+ - name: Generate coverage files
+ run: |
+ python ql/misc/scripts/library-coverage/generate-report.py ci ql ql
- - name: Generate coverage files
- run: |
- PATH="$PATH:codeql-cli/codeql" python ql/misc/scripts/library-coverage/generate-report.py ci ql ql
-
- - name: Create pull request with changes
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- python ql/misc/scripts/library-coverage/create-pr.py ql "$GITHUB_REPOSITORY"
+ - name: Create pull request with changes
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ python ql/misc/scripts/library-coverage/create-pr.py ql "$GITHUB_REPOSITORY"
diff --git a/.github/workflows/csv-coverage.yml b/.github/workflows/csv-coverage.yml
index 9a308d50265..e330490b69b 100644
--- a/.github/workflows/csv-coverage.yml
+++ b/.github/workflows/csv-coverage.yml
@@ -4,46 +4,39 @@ on:
workflow_dispatch:
inputs:
qlModelShaOverride:
- description: 'github/codeql repo SHA used for looking up the CSV models'
+ description: "github/codeql repo SHA used for looking up the CSV models"
required: false
jobs:
build:
-
runs-on: ubuntu-latest
steps:
- - name: Clone self (github/codeql)
- uses: actions/checkout@v3
- with:
- path: script
- - name: Clone self (github/codeql) for analysis
- uses: actions/checkout@v3
- with:
- path: codeqlModels
- ref: ${{ github.event.inputs.qlModelShaOverride || github.ref }}
- - name: Set up Python 3.8
- uses: actions/setup-python@v4
- with:
- python-version: 3.8
- - name: Download CodeQL CLI
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- run: |
- gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip"
- - name: Unzip CodeQL CLI
- run: unzip -d codeql-cli codeql-linux64.zip
- - name: Build modeled package list
- run: |
- PATH="$PATH:codeql-cli/codeql" python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script
- - name: Upload CSV package list
- uses: actions/upload-artifact@v3
- with:
- name: framework-coverage-csv
- path: framework-coverage-*.csv
- - name: Upload RST package list
- uses: actions/upload-artifact@v3
- with:
- name: framework-coverage-rst
- path: framework-coverage-*.rst
-
+ - name: Clone self (github/codeql)
+ uses: actions/checkout@v3
+ with:
+ path: script
+ - name: Clone self (github/codeql) for analysis
+ uses: actions/checkout@v3
+ with:
+ path: codeqlModels
+ ref: ${{ github.event.inputs.qlModelShaOverride || github.ref }}
+ - name: Set up Python 3.8
+ uses: actions/setup-python@v4
+ with:
+ python-version: 3.8
+ - name: Download CodeQL CLI
+ uses: ./script/.github/actions/fetch-codeql
+ - name: Build modeled package list
+ run: |
+ python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script
+ - name: Upload CSV package list
+ uses: actions/upload-artifact@v3
+ with:
+ name: framework-coverage-csv
+ path: framework-coverage-*.csv
+ - name: Upload RST package list
+ uses: actions/upload-artifact@v3
+ with:
+ name: framework-coverage-rst
+ path: framework-coverage-*.rst
diff --git a/.github/workflows/go-tests.yml b/.github/workflows/go-tests.yml
index ca126d1a3ee..26055bd71da 100644
--- a/.github/workflows/go-tests.yml
+++ b/.github/workflows/go-tests.yml
@@ -4,159 +4,111 @@ on:
paths:
- "go/**"
- .github/workflows/go-tests.yml
+ - .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
jobs:
-
test-linux:
name: Test Linux (Ubuntu)
runs-on: ubuntu-latest
steps:
+ - name: Set up Go 1.18.1
+ uses: actions/setup-go@v3
+ with:
+ go-version: 1.18.1
+ id: go
- - name: Set up Go 1.18.1
- uses: actions/setup-go@v3
- with:
- go-version: 1.18.1
- id: go
+ - name: Check out code
+ uses: actions/checkout@v2
- - name: Set up CodeQL CLI
- run: |
- echo "Removing old CodeQL Directory..."
- rm -rf $HOME/codeql
- echo "Done"
- cd $HOME
- echo "Downloading CodeQL CLI..."
- LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1)
- gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST"
- echo "Done"
- echo "Unpacking CodeQL CLI..."
- unzip -q codeql-linux64.zip
- rm -f codeql-linux64.zip
- echo "Done"
- env:
- GITHUB_TOKEN: ${{ github.token }}
+ - name: Set up CodeQL CLI
+ uses: ./.github/actions/fetch-codeql
- - name: Check out code
- uses: actions/checkout@v2
+ - name: Enable problem matchers in repository
+ shell: bash
+ run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- - 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: Build
- run: |
- cd go
- env PATH=$PATH:$HOME/codeql make
+ - name: Check that all QL and Go code is autoformatted
+ run: |
+ cd go
+ make check-formatting
- - name: Check that all QL and Go code is autoformatted
- run: |
- cd go
- env PATH=$PATH:$HOME/codeql make check-formatting
+ - name: Compile qhelp files to markdown
+ run: |
+ cd go
+ env QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown
- - name: Compile qhelp files to markdown
- run: |
- cd go
- env PATH=$PATH:$HOME/codeql QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown
+ - name: Upload qhelp markdown
+ uses: actions/upload-artifact@v2
+ with:
+ name: qhelp-markdown
+ path: go/qhelp-out/**/*.md
- - name: Upload qhelp markdown
- uses: actions/upload-artifact@v2
- with:
- name: qhelp-markdown
- path: go/qhelp-out/**/*.md
-
- - name: Test
- run: |
- cd go
- env PATH=$PATH:$HOME/codeql make test
+ - name: Test
+ run: |
+ cd go
+ make test
test-mac:
name: Test MacOS
- runs-on: macOS-latest
+ runs-on: macos-latest
steps:
- - name: Set up Go 1.18.1
- uses: actions/setup-go@v3
- with:
- go-version: 1.18.1
- id: go
+ - name: Set up Go 1.18.1
+ uses: actions/setup-go@v3
+ with:
+ go-version: 1.18.1
+ id: go
- - name: Set up CodeQL CLI
- run: |
- echo "Removing old CodeQL Directory..."
- rm -rf $HOME/codeql
- echo "Done"
- cd $HOME
- echo "Downloading CodeQL CLI..."
- LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1)
- gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-osx64.zip "$LATEST"
- echo "Done"
- echo "Unpacking CodeQL CLI..."
- unzip -q codeql-osx64.zip
- rm -f codeql-osx64.zip
- echo "Done"
- env:
- GITHUB_TOKEN: ${{ github.token }}
+ - name: Check out code
+ uses: actions/checkout@v2
- - 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: Enable problem matchers in repository
+ shell: bash
+ run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- - name: Build
- run: |
- cd go
- env PATH=$PATH:$HOME/codeql make
+ - name: Build
+ run: |
+ cd go
+ make
- - name: Test
- run: |
- cd go
- env PATH=$PATH:$HOME/codeql make test
+ - name: Test
+ run: |
+ cd go
+ make test
test-win:
name: Test Windows
runs-on: windows-2019
steps:
- - name: Set up Go 1.18.1
- uses: actions/setup-go@v3
- with:
- go-version: 1.18.1
- id: go
+ - name: Set up Go 1.18.1
+ uses: actions/setup-go@v3
+ with:
+ go-version: 1.18.1
+ id: go
- - name: Set up CodeQL CLI
- run: |
- echo "Removing old CodeQL Directory..."
- rm -rf $HOME/codeql
- echo "Done"
- cd "$HOME"
- echo "Downloading CodeQL CLI..."
- LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1)
- gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-win64.zip "$LATEST"
- echo "Done"
- echo "Unpacking CodeQL CLI..."
- unzip -q -o codeql-win64.zip
- unzip -q -o codeql-win64.zip codeql/codeql.exe
- rm -f codeql-win64.zip
- echo "Done"
- env:
- GITHUB_TOKEN: ${{ github.token }}
- shell:
- bash
+ - name: Check out code
+ uses: actions/checkout@v2
- - 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: Enable problem matchers in repository
+ shell: bash
+ run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;'
- - name: Build
- run: |
- $Env:Path += ";$HOME\codeql"
- cd go
- make
+ - name: Build
+ run: |
+ cd go
+ make
- - name: Test
- run: |
- $Env:Path += ";$HOME\codeql"
- cd go
- make test
+ - name: Test
+ run: |
+ cd go
+ make test
diff --git a/.github/workflows/js-ml-tests.yml b/.github/workflows/js-ml-tests.yml
index 65db215d8c3..c932432530b 100644
--- a/.github/workflows/js-ml-tests.yml
+++ b/.github/workflows/js-ml-tests.yml
@@ -5,6 +5,7 @@ on:
paths:
- "javascript/ql/experimental/adaptivethreatmodeling/**"
- .github/workflows/js-ml-tests.yml
+ - .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
@@ -13,6 +14,7 @@ on:
paths:
- "javascript/ql/experimental/adaptivethreatmodeling/**"
- .github/workflows/js-ml-tests.yml
+ - .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
workflow_dispatch:
diff --git a/.github/workflows/mad_regenerate-models.yml b/.github/workflows/mad_regenerate-models.yml
index d1d7e6e3791..0abc8936911 100644
--- a/.github/workflows/mad_regenerate-models.yml
+++ b/.github/workflows/mad_regenerate-models.yml
@@ -9,6 +9,7 @@ on:
- main
paths:
- ".github/workflows/mad_regenerate-models.yml"
+ - ".github/actions/fetch-codeql/action.yml"
jobs:
regenerate-models:
diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml
index 6b4f6a0abee..7c3b8ccb78c 100644
--- a/.github/workflows/ql-for-ql-build.yml
+++ b/.github/workflows/ql-for-ql-build.yml
@@ -10,16 +10,16 @@ env:
CARGO_TERM_COLOR: always
jobs:
- queries:
- runs-on: ubuntu-latest
+ analyze:
+ runs-on: ubuntu-latest-xl
steps:
+ ### Build the queries ###
- uses: actions/checkout@v3
- name: Find codeql
id: find-codeql
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
languages: javascript # does not matter
- tools: latest
- name: Get CodeQL version
id: get-codeql-version
run: |
@@ -40,6 +40,7 @@ jobs:
"${CODEQL}" pack create
cd .codeql/pack/codeql/ql/0.0.0
zip "${PACKZIP}" -r .
+ rm -rf *
env:
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
PACKZIP: ${{ runner.temp }}/query-pack.zip
@@ -49,14 +50,7 @@ jobs:
name: query-pack-zip
path: ${{ runner.temp }}/query-pack.zip
- extractors:
- strategy:
- fail-fast: false
-
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
+ ### Build the extractor ###
- name: Cache entire extractor
id: cache-extractor
uses: actions/cache@v3
@@ -100,15 +94,8 @@ jobs:
ql/target/release/ql-extractor
ql/target/release/ql-extractor.exe
retention-days: 1
- package:
- runs-on: ubuntu-latest
- needs:
- - extractors
- - queries
-
- steps:
- - uses: actions/checkout@v3
+ ### Package the queries and extractor ###
- uses: actions/download-artifact@v3
with:
name: query-pack-zip
@@ -131,21 +118,14 @@ jobs:
fi
cd pack
zip -rq ../codeql-ql.zip .
+ rm -rf *
- uses: actions/upload-artifact@v3
with:
name: codeql-ql-pack
path: codeql-ql.zip
retention-days: 1
- analyze:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- folder: [cpp, csharp, java, javascript, python, ql, ruby, swift, go]
- needs:
- - package
-
- steps:
+ ### Run the analysis ###
- name: Download pack
uses: actions/download-artifact@v3
with:
@@ -165,14 +145,11 @@ jobs:
env:
PACK: ${{ runner.temp }}/pack
- - name: Checkout repository
- uses: actions/checkout@v3
- name: Create CodeQL config file
run: |
- echo "paths:" > ${CONF}
- echo " - ${FOLDER}" >> ${CONF}
echo "paths-ignore:" >> ${CONF}
echo " - ql/ql/test" >> ${CONF}
+ echo " - \"*/ql/lib/upgrades/\"" >> ${CONF}
echo "disable-default-queries: true" >> ${CONF}
echo "packs:" >> ${CONF}
echo " - codeql/ql" >> ${CONF}
@@ -180,24 +157,34 @@ jobs:
cat ${CONF}
env:
CONF: ./ql-for-ql-config.yml
- FOLDER: ${{ matrix.folder }}
- name: Initialize CodeQL
uses: github/codeql-action/init@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
languages: ql
db-location: ${{ runner.temp }}/db
config-file: ./ql-for-ql-config.yml
- tools: latest
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@aa93aea877e5fb8841bcb1193f672abf6e9f2980
with:
- category: "ql-for-ql-${{ matrix.folder }}"
+ category: "ql-for-ql"
- name: Copy sarif file to CWD
- run: cp ../results/ql.sarif ./${{ matrix.folder }}.sarif
+ run: cp ../results/ql.sarif ./ql-for-ql.sarif
+ - name: Fixup the $scema in sarif # Until https://github.com/microsoft/sarif-vscode-extension/pull/436/ is part in a stable release
+ run: |
+ sed -i 's/\$schema.*/\$schema": "https:\/\/raw.githubusercontent.com\/oasis-tcs\/sarif-spec\/master\/Schemata\/sarif-schema-2.1.0",/' ql-for-ql.sarif
- name: Sarif as artifact
uses: actions/upload-artifact@v3
with:
- name: ${{ matrix.folder }}.sarif
- path: ${{ matrix.folder }}.sarif
-
+ name: ql-for-ql.sarif
+ path: ql-for-ql.sarif
+ - name: Split out the sarif file into langs
+ run: |
+ mkdir split-sarif
+ node ./ql/scripts/split-sarif.js ql-for-ql.sarif split-sarif
+ - name: Upload langs as artifacts
+ uses: actions/upload-artifact@v3
+ with:
+ name: ql-for-ql-langs
+ path: split-sarif
+ retention-days: 1
\ No newline at end of file
diff --git a/.github/workflows/ql-for-ql-dataset_measure.yml b/.github/workflows/ql-for-ql-dataset_measure.yml
index cf3b696f3b8..a5ed2e9b266 100644
--- a/.github/workflows/ql-for-ql-dataset_measure.yml
+++ b/.github/workflows/ql-for-ql-dataset_measure.yml
@@ -36,7 +36,7 @@ jobs:
ql/target
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
- name: Build Extractor
- run: cd ql; env "PATH=$PATH:`dirname ${CODEQL}`" ./create-extractor-pack.sh
+ run: cd ql; env "PATH=$PATH:`dirname ${CODEQL}`" ./scripts/create-extractor-pack.sh
env:
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
- name: Checkout ${{ matrix.repo }}
diff --git a/.github/workflows/ql-for-ql-tests.yml b/.github/workflows/ql-for-ql-tests.yml
index 3b0a4963b79..b016f21f2b9 100644
--- a/.github/workflows/ql-for-ql-tests.yml
+++ b/.github/workflows/ql-for-ql-tests.yml
@@ -36,7 +36,7 @@ jobs:
run: |
cd ql;
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
- env "PATH=$PATH:$codeqlpath" ./create-extractor-pack.sh
+ env "PATH=$PATH:$codeqlpath" ./scripts/create-extractor-pack.sh
- name: Run QL tests
run: |
"${CODEQL}" test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ql/extractor-pack" --consistency-queries ql/ql/consistency-queries ql/ql/test
diff --git a/.github/workflows/query-list.yml b/.github/workflows/query-list.yml
index 9416b740c99..efb295dfcf8 100644
--- a/.github/workflows/query-list.yml
+++ b/.github/workflows/query-list.yml
@@ -10,6 +10,7 @@ on:
pull_request:
paths:
- '.github/workflows/query-list.yml'
+ - '.github/actions/fetch-codeql/action.yml'
- 'misc/scripts/generate-code-scanning-query-list.py'
jobs:
@@ -29,8 +30,6 @@ jobs:
- name: Download CodeQL CLI
#Â Look under the `codeql` directory, as this is where we checked out the `github/codeql` repo
uses: ./codeql/.github/actions/fetch-codeql
- - name: Unzip CodeQL CLI
- run: unzip -d codeql-cli codeql-linux64.zip
- name: Build code scanning query list
run: |
python codeql/misc/scripts/generate-code-scanning-query-list.py > code-scanning-query-list.csv
diff --git a/.github/workflows/ruby-build.yml b/.github/workflows/ruby-build.yml
index c402312db0e..6ad627aab48 100644
--- a/.github/workflows/ruby-build.yml
+++ b/.github/workflows/ruby-build.yml
@@ -5,6 +5,7 @@ on:
paths:
- "ruby/**"
- .github/workflows/ruby-build.yml
+ - .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
@@ -13,6 +14,7 @@ on:
paths:
- "ruby/**"
- .github/workflows/ruby-build.yml
+ - .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
@@ -90,19 +92,14 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Fetch CodeQL
- run: |
- LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1)
- gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST"
- unzip -q codeql-linux64.zip
- env:
- GITHUB_TOKEN: ${{ github.token }}
+ uses: ./.github/actions/fetch-codeql
- name: Build Query Pack
run: |
- codeql/codeql pack create ql/lib --output target/packs
- codeql/codeql pack install ql/src
- codeql/codeql pack create ql/src --output target/packs
+ codeql pack create ql/lib --output target/packs
+ codeql pack install ql/src
+ codeql pack create ql/src --output target/packs
PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*)
- codeql/codeql generate query-help --format=sarifv2.1.0 --output="${PACK_FOLDER}/rules.sarif" ql/src
+ 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}/{}" \;)
- uses: actions/upload-artifact@v3
with:
@@ -179,19 +176,15 @@ jobs:
runs-on: ${{ matrix.os }}
needs: [package]
steps:
+ - uses: actions/checkout@v3
+ - name: Fetch CodeQL
+ uses: ./.github/actions/fetch-codeql
+
- uses: actions/checkout@v3
with:
repository: Shopify/example-ruby-app
ref: 67a0decc5eb550f3a9228eda53925c3afd40dfe9
- - name: Fetch CodeQL
- shell: bash
- run: |
- LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1)
- gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql.zip "$LATEST"
- unzip -q codeql.zip
- env:
- GITHUB_TOKEN: ${{ github.token }}
- working-directory: ${{ runner.temp }}
+
- name: Download Ruby bundle
uses: actions/download-artifact@v3
with:
@@ -215,12 +208,12 @@ jobs:
- name: Run QL test
shell: bash
run: |
- "${{ runner.temp }}/codeql/codeql" test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" .
+ codeql test run --search-path "${{ runner.temp }}/ruby-bundle" --additional-packs "${{ runner.temp }}/ruby-bundle" .
- name: Create database
shell: bash
run: |
- "${{ runner.temp }}/codeql/codeql" database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root . ../database
+ codeql database create --search-path "${{ runner.temp }}/ruby-bundle" --language ruby --source-root . ../database
- name: Analyze database
shell: bash
run: |
- "${{ runner.temp }}/codeql/codeql" database analyze --search-path "${{ runner.temp }}/ruby-bundle" --format=sarifv2.1.0 --output=out.sarif ../database ruby-code-scanning.qls
+ codeql database analyze --search-path "${{ runner.temp }}/ruby-bundle" --format=sarifv2.1.0 --output=out.sarif ../database ruby-code-scanning.qls
diff --git a/.github/workflows/ruby-qltest.yml b/.github/workflows/ruby-qltest.yml
index 0cf8860d8f1..97235b722ba 100644
--- a/.github/workflows/ruby-qltest.yml
+++ b/.github/workflows/ruby-qltest.yml
@@ -5,6 +5,7 @@ on:
paths:
- "ruby/**"
- .github/workflows/ruby-qltest.yml
+ - .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
@@ -13,6 +14,7 @@ on:
paths:
- "ruby/**"
- .github/workflows/ruby-qltest.yml
+ - .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
diff --git a/.github/workflows/swift-codegen.yml b/.github/workflows/swift-codegen.yml
index 46a27709717..5700045430d 100644
--- a/.github/workflows/swift-codegen.yml
+++ b/.github/workflows/swift-codegen.yml
@@ -5,6 +5,7 @@ on:
paths:
- "swift/**"
- .github/workflows/swift-codegen.yml
+ - .github/actions/fetch-codeql/action.yml
branches:
- main
diff --git a/.github/workflows/swift-integration-tests.yml b/.github/workflows/swift-integration-tests.yml
new file mode 100644
index 00000000000..4d4248b64e3
--- /dev/null
+++ b/.github/workflows/swift-integration-tests.yml
@@ -0,0 +1,35 @@
+name: "Swift: Run Integration Tests"
+
+on:
+ pull_request:
+ paths:
+ - "swift/**"
+ - .github/workflows/swift-integration-tests.yml
+ - .github/actions/fetch-codeql/action.yml
+ - codeql-workspace.yml
+ branches:
+ - main
+defaults:
+ run:
+ working-directory: swift
+
+jobs:
+ integration-tests:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os:
+ - ubuntu-20.04
+# - macos-latest TODO
+ steps:
+ - uses: actions/checkout@v3
+ - uses: ./.github/actions/fetch-codeql
+ - uses: bazelbuild/setup-bazelisk@v2
+ - uses: actions/setup-python@v3
+ - name: Build Swift extractor
+ run: |
+ bazel run //swift:create-extractor-pack
+ - name: Run integration tests
+ run: |
+ python integration-tests/runner.py
diff --git a/.github/workflows/swift-qltest.yml b/.github/workflows/swift-qltest.yml
index 915e1f331a5..3cbcf629c98 100644
--- a/.github/workflows/swift-qltest.yml
+++ b/.github/workflows/swift-qltest.yml
@@ -5,6 +5,7 @@ on:
paths:
- "swift/**"
- .github/workflows/swift-qltest.yml
+ - .github/actions/fetch-codeql/action.yml
- codeql-workspace.yml
branches:
- main
diff --git a/.github/workflows/validate-change-notes.yml b/.github/workflows/validate-change-notes.yml
index 798913746be..44e0dc6df29 100644
--- a/.github/workflows/validate-change-notes.yml
+++ b/.github/workflows/validate-change-notes.yml
@@ -5,6 +5,7 @@ on:
paths:
- "*/ql/*/change-notes/**/*"
- ".github/workflows/validate-change-notes.yml"
+ - ".github/actions/fetch-codeql/action.yml"
branches:
- main
- "rc/*"
@@ -12,6 +13,7 @@ on:
paths:
- "*/ql/*/change-notes/**/*"
- ".github/workflows/validate-change-notes.yml"
+ - ".github/actions/fetch-codeql/action.yml"
jobs:
check-change-note:
diff --git a/CODEOWNERS b/CODEOWNERS
index da71d1ec5d8..1754d58af63 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -42,3 +42,4 @@ WORKSPACE.bazel @github/codeql-ci-reviewers
/.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers
/.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers
/.github/workflows/ruby-* @github/codeql-ruby
+/.github/workflows/swift-* @github/codeql-c
diff --git a/config/identical-files.json b/config/identical-files.json
index 77e39399cec..403e6b91c2f 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -392,7 +392,8 @@
"python/ql/test/TestUtilities/InlineExpectationsTest.qll",
"ruby/ql/test/TestUtilities/InlineExpectationsTest.qll",
"ql/ql/test/TestUtilities/InlineExpectationsTest.qll",
- "go/ql/test/TestUtilities/InlineExpectationsTest.qll"
+ "go/ql/test/TestUtilities/InlineExpectationsTest.qll",
+ "swift/ql/test/TestUtilities/InlineExpectationsTest.qll"
],
"C++ ExternalAPIs": [
"cpp/ql/src/Security/CWE/CWE-020/ExternalAPIs.qll",
@@ -453,11 +454,11 @@
"python/ql/src/Lexical/CommentedOutCodeReferences.inc.qhelp"
],
"IDE Contextual Queries": [
- "cpp/ql/src/IDEContextual.qll",
- "csharp/ql/src/IDEContextual.qll",
- "java/ql/src/IDEContextual.qll",
- "javascript/ql/src/IDEContextual.qll",
- "python/ql/src/analysis/IDEContextual.qll"
+ "cpp/ql/lib/IDEContextual.qll",
+ "csharp/ql/lib/IDEContextual.qll",
+ "java/ql/lib/IDEContextual.qll",
+ "javascript/ql/lib/IDEContextual.qll",
+ "python/ql/lib/analysis/IDEContextual.qll"
],
"SSA C#": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImplCommon.qll",
diff --git a/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/exprs.ql b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/exprs.ql
new file mode 100644
index 00000000000..d00685e7cc6
--- /dev/null
+++ b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/exprs.ql
@@ -0,0 +1,17 @@
+class Expr extends @expr {
+ string toString() { none() }
+}
+
+class Location extends @location_expr {
+ string toString() { none() }
+}
+
+predicate isExprWithNewBuiltin(Expr expr) {
+ exists(int kind | exprs(expr, kind, _) | 330 <= kind and kind <= 334)
+}
+
+from Expr expr, int kind, int kind_new, Location location
+where
+ exprs(expr, kind, location) and
+ if isExprWithNewBuiltin(expr) then kind_new = 0 else kind_new = kind
+select expr, kind_new, location
diff --git a/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/old.dbscheme b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/old.dbscheme
new file mode 100644
index 00000000000..23f7cbb88a4
--- /dev/null
+++ b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/old.dbscheme
@@ -0,0 +1,2125 @@
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ /**
+ * An invocation of the compiler. Note that more than one file may
+ * be compiled per invocation. For example, this command compiles
+ * three source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ */
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | *path to extractor*
+ * 1 | `--mimic`
+ * 2 | `/usr/bin/gcc`
+ * 3 | `-c`
+ * 4 | f1.c
+ * 5 | f2.c
+ * 6 | f3.c
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.c
+ * 1 | f2.c
+ * 2 | f3.c
+ *
+ * Note that even if those files `#include` headers, those headers
+ * do not appear as rows.
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+
+/**
+ * External data, loaded from CSV files during snapshot creation. See
+ * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data)
+ * for more information.
+ */
+externalData(
+ int id : @externalDataElement,
+ string path : string ref,
+ int column: int ref,
+ string value : string ref
+);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Information about packages that provide code used during compilation.
+ * The `id` is just a unique identifier.
+ * The `namespace` is typically the name of the package manager that
+ * provided the package (e.g. "dpkg" or "yum").
+ * The `package_name` is the name of the package, and `version` is its
+ * version (as a string).
+ */
+external_packages(
+ unique int id: @external_package,
+ string namespace : string ref,
+ string package_name : string ref,
+ string version : string ref
+);
+
+/**
+ * Holds if File `fileid` was provided by package `package`.
+ */
+header_to_external_package(
+ int fileid : @file ref,
+ int package : @external_package ref
+);
+
+/*
+ * Version history
+ */
+
+svnentries(
+ unique int id : @svnentry,
+ string revision : string ref,
+ string author : string ref,
+ date revisionDate : date ref,
+ int changeSize : int ref
+)
+
+svnaffectedfiles(
+ int id : @svnentry ref,
+ int file : @file ref,
+ string action : string ref
+)
+
+svnentrymsg(
+ unique int id : @svnentry ref,
+ string message : string ref
+)
+
+svnchurn(
+ int commit : @svnentry ref,
+ int file : @file ref,
+ int addedLines : int ref,
+ int deletedLines : int ref
+)
+
+/*
+ * C++ dbscheme
+ */
+
+@location = @location_stmt | @location_expr | @location_default ;
+
+/**
+ * The location of an element that is not an expression or a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_default(
+ /** The location of an element that is not an expression or a statement. */
+ unique int id: @location_default,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_stmt(
+ /** The location of a statement. */
+ unique int id: @location_stmt,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of an expression.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_expr(
+ /** The location of an expression. */
+ unique int id: @location_expr,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/** An element for which line-count information is available. */
+@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location_default ref
+);
+
+files(
+ unique int id: @file,
+ string name: string ref
+);
+
+folders(
+ unique int id: @folder,
+ string name: string ref
+);
+
+@container = @folder | @file
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref
+);
+
+fileannotations(
+ int id: @file ref,
+ int kind: int ref,
+ string name: string ref,
+ string value: string ref
+);
+
+inmacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+affectedbymacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+/*
+ case @macroinvocations.kind of
+ 1 = macro expansion
+ | 2 = other macro reference
+ ;
+*/
+macroinvocations(
+ unique int id: @macroinvocation,
+ int macro_id: @ppd_define ref,
+ int location: @location_default ref,
+ int kind: int ref
+);
+
+macroparent(
+ unique int id: @macroinvocation ref,
+ int parent_id: @macroinvocation ref
+);
+
+// a macroinvocation may be part of another location
+// the way to find a constant expression that uses a macro
+// is thus to find a constant expression that has a location
+// to which a macro invocation is bound
+macrolocationbind(
+ int id: @macroinvocation ref,
+ int location: @location ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_unexpanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_expanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+/*
+ case @function.kind of
+ 1 = normal
+ | 2 = constructor
+ | 3 = destructor
+ | 4 = conversion
+ | 5 = operator
+ | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk
+ ;
+*/
+functions(
+ unique int id: @function,
+ string name: string ref,
+ int kind: int ref
+);
+
+function_entry_point(int id: @function ref, unique int entry_point: @stmt ref);
+
+function_return_type(int id: @function ref, int return_type: @type ref);
+
+/** If `function` is a coroutine, then this gives the
+ std::experimental::resumable_traits instance associated with it,
+ and the variables representing the `handle` and `promise` for it. */
+coroutine(
+ unique int function: @function ref,
+ int traits: @type ref,
+ int handle: @variable ref,
+ int promise: @variable ref
+);
+
+/** The `new` function used for allocating the coroutine state, if any. */
+coroutine_new(
+ unique int function: @function ref,
+ int new: @function ref
+);
+
+/** The `delete` function used for deallocating the coroutine state, if any. */
+coroutine_delete(
+ unique int function: @function ref,
+ int delete: @function ref
+);
+
+purefunctions(unique int id: @function ref);
+
+function_deleted(unique int id: @function ref);
+
+function_defaulted(unique int id: @function ref);
+
+member_function_this_type(unique int id: @function ref, int this_type: @type ref);
+
+#keyset[id, type_id]
+fun_decls(
+ int id: @fun_decl,
+ int function: @function ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+fun_def(unique int id: @fun_decl ref);
+fun_specialized(unique int id: @fun_decl ref);
+fun_implicit(unique int id: @fun_decl ref);
+fun_decl_specifiers(
+ int id: @fun_decl ref,
+ string name: string ref
+)
+#keyset[fun_decl, index]
+fun_decl_throws(
+ int fun_decl: @fun_decl ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+/* an empty throw specification is different from none */
+fun_decl_empty_throws(unique int fun_decl: @fun_decl ref);
+fun_decl_noexcept(
+ int fun_decl: @fun_decl ref,
+ int constant: @expr ref
+);
+fun_decl_empty_noexcept(int fun_decl: @fun_decl ref);
+fun_decl_typedef_type(
+ unique int fun_decl: @fun_decl ref,
+ int typedeftype_id: @usertype ref
+);
+
+param_decl_bind(
+ unique int id: @var_decl ref,
+ int index: int ref,
+ int fun_decl: @fun_decl ref
+);
+
+#keyset[id, type_id]
+var_decls(
+ int id: @var_decl,
+ int variable: @variable ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+var_def(unique int id: @var_decl ref);
+var_decl_specifiers(
+ int id: @var_decl ref,
+ string name: string ref
+)
+is_structured_binding(unique int id: @variable ref);
+
+type_decls(
+ unique int id: @type_decl,
+ int type_id: @type ref,
+ int location: @location_default ref
+);
+type_def(unique int id: @type_decl ref);
+type_decl_top(
+ unique int type_decl: @type_decl ref
+);
+
+namespace_decls(
+ unique int id: @namespace_decl,
+ int namespace_id: @namespace ref,
+ int location: @location_default ref,
+ int bodylocation: @location_default ref
+);
+
+usings(
+ unique int id: @using,
+ int element_id: @element ref,
+ int location: @location_default ref
+);
+
+/** The element which contains the `using` declaration. */
+using_container(
+ int parent: @element ref,
+ int child: @using ref
+);
+
+static_asserts(
+ unique int id: @static_assert,
+ int condition : @expr ref,
+ string message : string ref,
+ int location: @location_default ref,
+ int enclosing : @element ref
+);
+
+// each function has an ordered list of parameters
+#keyset[id, type_id]
+#keyset[function, index, type_id]
+params(
+ int id: @parameter,
+ int function: @functionorblock ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+overrides(int new: @function ref, int old: @function ref);
+
+#keyset[id, type_id]
+membervariables(
+ int id: @membervariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+globalvariables(
+ int id: @globalvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+localvariables(
+ int id: @localvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+autoderivation(
+ unique int var: @variable ref,
+ int derivation_type: @type ref
+);
+
+enumconstants(
+ unique int id: @enumconstant,
+ int parent: @usertype ref,
+ int index: int ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+
+@variable = @localscopevariable | @globalvariable | @membervariable;
+
+@localscopevariable = @localvariable | @parameter;
+
+/*
+ Built-in types are the fundamental types, e.g., integral, floating, and void.
+
+ case @builtintype.kind of
+ 1 = error
+ | 2 = unknown
+ | 3 = void
+ | 4 = boolean
+ | 5 = char
+ | 6 = unsigned_char
+ | 7 = signed_char
+ | 8 = short
+ | 9 = unsigned_short
+ | 10 = signed_short
+ | 11 = int
+ | 12 = unsigned_int
+ | 13 = signed_int
+ | 14 = long
+ | 15 = unsigned_long
+ | 16 = signed_long
+ | 17 = long_long
+ | 18 = unsigned_long_long
+ | 19 = signed_long_long
+ | 20 = __int8 // Microsoft-specific
+ | 21 = __int16 // Microsoft-specific
+ | 22 = __int32 // Microsoft-specific
+ | 23 = __int64 // Microsoft-specific
+ | 24 = float
+ | 25 = double
+ | 26 = long_double
+ | 27 = _Complex_float // C99-specific
+ | 28 = _Complex_double // C99-specific
+ | 29 = _Complex_long double // C99-specific
+ | 30 = _Imaginary_float // C99-specific
+ | 31 = _Imaginary_double // C99-specific
+ | 32 = _Imaginary_long_double // C99-specific
+ | 33 = wchar_t // Microsoft-specific
+ | 34 = decltype_nullptr // C++11
+ | 35 = __int128
+ | 36 = unsigned___int128
+ | 37 = signed___int128
+ | 38 = __float128
+ | 39 = _Complex___float128
+ | 40 = _Decimal32
+ | 41 = _Decimal64
+ | 42 = _Decimal128
+ | 43 = char16_t
+ | 44 = char32_t
+ | 45 = _Float32
+ | 46 = _Float32x
+ | 47 = _Float64
+ | 48 = _Float64x
+ | 49 = _Float128
+ | 50 = _Float128x
+ | 51 = char8_t
+ ;
+*/
+builtintypes(
+ unique int id: @builtintype,
+ string name: string ref,
+ int kind: int ref,
+ int size: int ref,
+ int sign: int ref,
+ int alignment: int ref
+);
+
+/*
+ Derived types are types that are directly derived from existing types and
+ point to, refer to, transform type data to return a new type.
+
+ case @derivedtype.kind of
+ 1 = pointer
+ | 2 = reference
+ | 3 = type_with_specifiers
+ | 4 = array
+ | 5 = gnu_vector
+ | 6 = routineptr
+ | 7 = routinereference
+ | 8 = rvalue_reference // C++11
+// ... 9 type_conforming_to_protocols deprecated
+ | 10 = block
+ ;
+*/
+derivedtypes(
+ unique int id: @derivedtype,
+ string name: string ref,
+ int kind: int ref,
+ int type_id: @type ref
+);
+
+pointerishsize(unique int id: @derivedtype ref,
+ int size: int ref,
+ int alignment: int ref);
+
+arraysizes(
+ unique int id: @derivedtype ref,
+ int num_elements: int ref,
+ int bytesize: int ref,
+ int alignment: int ref
+);
+
+typedefbase(
+ unique int id: @usertype ref,
+ int type_id: @type ref
+);
+
+/**
+ * An instance of the C++11 `decltype` operator. For example:
+ * ```
+ * int a;
+ * decltype(1+a) b;
+ * ```
+ * Here `expr` is `1+a`.
+ *
+ * Sometimes an additional pair of parentheses around the expression
+ * would change the semantics of this decltype, e.g.
+ * ```
+ * struct A { double x; };
+ * const A* a = new A();
+ * decltype( a->x ); // type is double
+ * decltype((a->x)); // type is const double&
+ * ```
+ * (Please consult the C++11 standard for more details).
+ * `parentheses_would_change_meaning` is `true` iff that is the case.
+ */
+#keyset[id, expr]
+decltypes(
+ int id: @decltype,
+ int expr: @expr ref,
+ int base_type: @type ref,
+ boolean parentheses_would_change_meaning: boolean ref
+);
+
+/*
+ case @usertype.kind of
+ 1 = struct
+ | 2 = class
+ | 3 = union
+ | 4 = enum
+ | 5 = typedef // classic C: typedef typedef type name
+ | 6 = template
+ | 7 = template_parameter
+ | 8 = template_template_parameter
+ | 9 = proxy_class // a proxy class associated with a template parameter
+// ... 10 objc_class deprecated
+// ... 11 objc_protocol deprecated
+// ... 12 objc_category deprecated
+ | 13 = scoped_enum
+ | 14 = using_alias // a using name = type style typedef
+ ;
+*/
+usertypes(
+ unique int id: @usertype,
+ string name: string ref,
+ int kind: int ref
+);
+
+usertypesize(
+ unique int id: @usertype ref,
+ int size: int ref,
+ int alignment: int ref
+);
+
+usertype_final(unique int id: @usertype ref);
+
+usertype_uuid(
+ unique int id: @usertype ref,
+ string uuid: string ref
+);
+
+mangled_name(
+ unique int id: @declaration ref,
+ int mangled_name : @mangledname
+);
+
+is_pod_class(unique int id: @usertype ref);
+is_standard_layout_class(unique int id: @usertype ref);
+
+is_complete(unique int id: @usertype ref);
+
+is_class_template(unique int id: @usertype ref);
+class_instantiation(
+ int to: @usertype ref,
+ int from: @usertype ref
+);
+class_template_argument(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+class_template_argument_value(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_proxy_class_for(
+ unique int id: @usertype ref,
+ unique int templ_param_id: @usertype ref
+);
+
+type_mentions(
+ unique int id: @type_mention,
+ int type_id: @type ref,
+ int location: @location ref,
+ // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there.
+ int kind: int ref
+);
+
+is_function_template(unique int id: @function ref);
+function_instantiation(
+ unique int to: @function ref,
+ int from: @function ref
+);
+function_template_argument(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+function_template_argument_value(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_variable_template(unique int id: @variable ref);
+variable_instantiation(
+ unique int to: @variable ref,
+ int from: @variable ref
+);
+variable_template_argument(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+variable_template_argument_value(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+/*
+ Fixed point types
+ precision(1) = short, precision(2) = default, precision(3) = long
+ is_unsigned(1) = unsigned is_unsigned(2) = signed
+ is_fract_type(1) = declared with _Fract
+ saturating(1) = declared with _Sat
+*/
+/* TODO
+fixedpointtypes(
+ unique int id: @fixedpointtype,
+ int precision: int ref,
+ int is_unsigned: int ref,
+ int is_fract_type: int ref,
+ int saturating: int ref);
+*/
+
+routinetypes(
+ unique int id: @routinetype,
+ int return_type: @type ref
+);
+
+routinetypeargs(
+ int routine: @routinetype ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+ptrtomembers(
+ unique int id: @ptrtomember,
+ int type_id: @type ref,
+ int class_id: @type ref
+);
+
+/*
+ specifiers for types, functions, and variables
+
+ "public",
+ "protected",
+ "private",
+
+ "const",
+ "volatile",
+ "static",
+
+ "pure",
+ "virtual",
+ "sealed", // Microsoft
+ "__interface", // Microsoft
+ "inline",
+ "explicit",
+
+ "near", // near far extension
+ "far", // near far extension
+ "__ptr32", // Microsoft
+ "__ptr64", // Microsoft
+ "__sptr", // Microsoft
+ "__uptr", // Microsoft
+ "dllimport", // Microsoft
+ "dllexport", // Microsoft
+ "thread", // Microsoft
+ "naked", // Microsoft
+ "microsoft_inline", // Microsoft
+ "forceinline", // Microsoft
+ "selectany", // Microsoft
+ "nothrow", // Microsoft
+ "novtable", // Microsoft
+ "noreturn", // Microsoft
+ "noinline", // Microsoft
+ "noalias", // Microsoft
+ "restrict", // Microsoft
+*/
+
+specifiers(
+ unique int id: @specifier,
+ unique string str: string ref
+);
+
+typespecifiers(
+ int type_id: @type ref,
+ int spec_id: @specifier ref
+);
+
+funspecifiers(
+ int func_id: @function ref,
+ int spec_id: @specifier ref
+);
+
+varspecifiers(
+ int var_id: @accessible ref,
+ int spec_id: @specifier ref
+);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ string name: string ref,
+ string name_space: string ref,
+ int location: @location_default ref
+);
+
+case @attribute.kind of
+ 0 = @gnuattribute
+| 1 = @stdattribute
+| 2 = @declspec
+| 3 = @msattribute
+| 4 = @alignas
+// ... 5 @objc_propertyattribute deprecated
+;
+
+attribute_args(
+ unique int id: @attribute_arg,
+ int kind: int ref,
+ int attribute: @attribute ref,
+ int index: int ref,
+ int location: @location_default ref
+);
+
+case @attribute_arg.kind of
+ 0 = @attribute_arg_empty
+| 1 = @attribute_arg_token
+| 2 = @attribute_arg_constant
+| 3 = @attribute_arg_type
+;
+
+attribute_arg_value(
+ unique int arg: @attribute_arg ref,
+ string value: string ref
+);
+attribute_arg_type(
+ unique int arg: @attribute_arg ref,
+ int type_id: @type ref
+);
+attribute_arg_name(
+ unique int arg: @attribute_arg ref,
+ string name: string ref
+);
+
+typeattributes(
+ int type_id: @type ref,
+ int spec_id: @attribute ref
+);
+
+funcattributes(
+ int func_id: @function ref,
+ int spec_id: @attribute ref
+);
+
+varattributes(
+ int var_id: @accessible ref,
+ int spec_id: @attribute ref
+);
+
+stmtattributes(
+ int stmt_id: @stmt ref,
+ int spec_id: @attribute ref
+);
+
+@type = @builtintype
+ | @derivedtype
+ | @usertype
+ /* TODO | @fixedpointtype */
+ | @routinetype
+ | @ptrtomember
+ | @decltype;
+
+unspecifiedtype(
+ unique int type_id: @type ref,
+ int unspecified_type_id: @type ref
+);
+
+member(
+ int parent: @type ref,
+ int index: int ref,
+ int child: @member ref
+);
+
+@enclosingfunction_child = @usertype | @variable | @namespace
+
+enclosingfunction(
+ unique int child: @enclosingfunction_child ref,
+ int parent: @function ref
+);
+
+derivations(
+ unique int derivation: @derivation,
+ int sub: @type ref,
+ int index: int ref,
+ int super: @type ref,
+ int location: @location_default ref
+);
+
+derspecifiers(
+ int der_id: @derivation ref,
+ int spec_id: @specifier ref
+);
+
+/**
+ * Contains the byte offset of the base class subobject within the derived
+ * class. Only holds for non-virtual base classes, but see table
+ * `virtual_base_offsets` for offsets of virtual base class subobjects.
+ */
+direct_base_offsets(
+ unique int der_id: @derivation ref,
+ int offset: int ref
+);
+
+/**
+ * Contains the byte offset of the virtual base class subobject for class
+ * `super` within a most-derived object of class `sub`. `super` can be either a
+ * direct or indirect base class.
+ */
+#keyset[sub, super]
+virtual_base_offsets(
+ int sub: @usertype ref,
+ int super: @usertype ref,
+ int offset: int ref
+);
+
+frienddecls(
+ unique int id: @frienddecl,
+ int type_id: @type ref,
+ int decl_id: @declaration ref,
+ int location: @location_default ref
+);
+
+@declaredtype = @usertype ;
+
+@declaration = @function
+ | @declaredtype
+ | @variable
+ | @enumconstant
+ | @frienddecl;
+
+@member = @membervariable
+ | @function
+ | @declaredtype
+ | @enumconstant;
+
+@locatable = @diagnostic
+ | @declaration
+ | @ppd_include
+ | @ppd_define
+ | @macroinvocation
+ /*| @funcall*/
+ | @xmllocatable
+ | @attribute
+ | @attribute_arg;
+
+@namedscope = @namespace | @usertype;
+
+@element = @locatable
+ | @file
+ | @folder
+ | @specifier
+ | @type
+ | @expr
+ | @namespace
+ | @initialiser
+ | @stmt
+ | @derivation
+ | @comment
+ | @preprocdirect
+ | @fun_decl
+ | @var_decl
+ | @type_decl
+ | @namespace_decl
+ | @using
+ | @namequalifier
+ | @specialnamequalifyingelement
+ | @static_assert
+ | @type_mention
+ | @lambdacapture;
+
+@exprparent = @element;
+
+comments(
+ unique int id: @comment,
+ string contents: string ref,
+ int location: @location_default ref
+);
+
+commentbinding(
+ int id: @comment ref,
+ int element: @element ref
+);
+
+exprconv(
+ int converted: @expr ref,
+ unique int conversion: @expr ref
+);
+
+compgenerated(unique int id: @element ref);
+
+/**
+ * `destructor_call` destructs the `i`'th entity that should be
+ * destructed following `element`. Note that entities should be
+ * destructed in reverse construction order, so for a given `element`
+ * these should be called from highest to lowest `i`.
+ */
+#keyset[element, destructor_call]
+#keyset[element, i]
+synthetic_destructor_call(
+ int element: @element ref,
+ int i: int ref,
+ int destructor_call: @routineexpr ref
+);
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref
+);
+
+namespace_inline(
+ unique int id: @namespace ref
+);
+
+namespacembrs(
+ int parentid: @namespace ref,
+ unique int memberid: @namespacembr ref
+);
+
+@namespacembr = @declaration | @namespace;
+
+exprparents(
+ int expr_id: @expr ref,
+ int child_index: int ref,
+ int parent_id: @exprparent ref
+);
+
+expr_isload(unique int expr_id: @expr ref);
+
+@cast = @c_style_cast
+ | @const_cast
+ | @dynamic_cast
+ | @reinterpret_cast
+ | @static_cast
+ ;
+
+/*
+case @conversion.kind of
+ 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast
+| 1 = @bool_conversion // conversion to 'bool'
+| 2 = @base_class_conversion // a derived-to-base conversion
+| 3 = @derived_class_conversion // a base-to-derived conversion
+| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member
+| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member
+| 6 = @glvalue_adjust // an adjustment of the type of a glvalue
+| 7 = @prvalue_adjust // an adjustment of the type of a prvalue
+;
+*/
+/**
+ * Describes the semantics represented by a cast expression. This is largely
+ * independent of the source syntax of the cast, so it is separate from the
+ * regular expression kind.
+ */
+conversionkinds(
+ unique int expr_id: @cast ref,
+ int kind: int ref
+);
+
+@conversion = @cast
+ | @array_to_pointer
+ | @parexpr
+ | @reference_to
+ | @ref_indirect
+ | @temp_init
+ ;
+
+/*
+case @funbindexpr.kind of
+ 0 = @normal_call // a normal call
+| 1 = @virtual_call // a virtual call
+| 2 = @adl_call // a call whose target is only found by ADL
+;
+*/
+iscall(unique int caller: @funbindexpr ref, int kind: int ref);
+
+numtemplatearguments(
+ unique int expr_id: @expr ref,
+ int num: int ref
+);
+
+specialnamequalifyingelements(
+ unique int id: @specialnamequalifyingelement,
+ unique string name: string ref
+);
+
+@namequalifiableelement = @expr | @namequalifier;
+@namequalifyingelement = @namespace
+ | @specialnamequalifyingelement
+ | @usertype;
+
+namequalifiers(
+ unique int id: @namequalifier,
+ unique int qualifiableelement: @namequalifiableelement ref,
+ int qualifyingelement: @namequalifyingelement ref,
+ int location: @location_default ref
+);
+
+varbind(
+ int expr: @varbindexpr ref,
+ int var: @accessible ref
+);
+
+funbind(
+ int expr: @funbindexpr ref,
+ int fun: @function ref
+);
+
+@any_new_expr = @new_expr
+ | @new_array_expr;
+
+@new_or_delete_expr = @any_new_expr
+ | @delete_expr
+ | @delete_array_expr;
+
+@prefix_crement_expr = @preincrexpr | @predecrexpr;
+
+@postfix_crement_expr = @postincrexpr | @postdecrexpr;
+
+@increment_expr = @preincrexpr | @postincrexpr;
+
+@decrement_expr = @predecrexpr | @postdecrexpr;
+
+@crement_expr = @increment_expr | @decrement_expr;
+
+@un_arith_op_expr = @arithnegexpr
+ | @unaryplusexpr
+ | @conjugation
+ | @realpartexpr
+ | @imagpartexpr
+ | @crement_expr
+ ;
+
+@un_bitwise_op_expr = @complementexpr;
+
+@un_log_op_expr = @notexpr;
+
+@un_op_expr = @address_of
+ | @indirect
+ | @un_arith_op_expr
+ | @un_bitwise_op_expr
+ | @builtinaddressof
+ | @vec_fill
+ | @un_log_op_expr
+ | @co_await
+ | @co_yield
+ ;
+
+@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr;
+
+@cmp_op_expr = @eq_op_expr | @rel_op_expr;
+
+@eq_op_expr = @eqexpr | @neexpr;
+
+@rel_op_expr = @gtexpr
+ | @ltexpr
+ | @geexpr
+ | @leexpr
+ | @spaceshipexpr
+ ;
+
+@bin_bitwise_op_expr = @lshiftexpr
+ | @rshiftexpr
+ | @andexpr
+ | @orexpr
+ | @xorexpr
+ ;
+
+@p_arith_op_expr = @paddexpr
+ | @psubexpr
+ | @pdiffexpr
+ ;
+
+@bin_arith_op_expr = @addexpr
+ | @subexpr
+ | @mulexpr
+ | @divexpr
+ | @remexpr
+ | @jmulexpr
+ | @jdivexpr
+ | @fjaddexpr
+ | @jfaddexpr
+ | @fjsubexpr
+ | @jfsubexpr
+ | @minexpr
+ | @maxexpr
+ | @p_arith_op_expr
+ ;
+
+@bin_op_expr = @bin_arith_op_expr
+ | @bin_bitwise_op_expr
+ | @cmp_op_expr
+ | @bin_log_op_expr
+ ;
+
+@op_expr = @un_op_expr
+ | @bin_op_expr
+ | @assign_expr
+ | @conditionalexpr
+ ;
+
+@assign_arith_expr = @assignaddexpr
+ | @assignsubexpr
+ | @assignmulexpr
+ | @assigndivexpr
+ | @assignremexpr
+ ;
+
+@assign_bitwise_expr = @assignandexpr
+ | @assignorexpr
+ | @assignxorexpr
+ | @assignlshiftexpr
+ | @assignrshiftexpr
+ | @assignpaddexpr
+ | @assignpsubexpr
+ ;
+
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr
+
+@assign_expr = @assignexpr | @assign_op_expr
+
+/*
+ case @allocator.form of
+ 0 = plain
+ | 1 = alignment
+ ;
+*/
+
+/**
+ * The allocator function associated with a `new` or `new[]` expression.
+ * The `form` column specified whether the allocation call contains an alignment
+ * argument.
+ */
+expr_allocator(
+ unique int expr: @any_new_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/*
+ case @deallocator.form of
+ 0 = plain
+ | 1 = size
+ | 2 = alignment
+ | 3 = size_and_alignment
+ ;
+*/
+
+/**
+ * The deallocator function associated with a `delete`, `delete[]`, `new`, or
+ * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the
+ * one used to free memory if the initialization throws an exception.
+ * The `form` column specifies whether the deallocation call contains a size
+ * argument, and alignment argument, or both.
+ */
+expr_deallocator(
+ unique int expr: @new_or_delete_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/**
+ * Holds if the `@conditionalexpr` is of the two operand form
+ * `guard ? : false`.
+ */
+expr_cond_two_operand(
+ unique int cond: @conditionalexpr ref
+);
+
+/**
+ * The guard of `@conditionalexpr` `guard ? true : false`
+ */
+expr_cond_guard(
+ unique int cond: @conditionalexpr ref,
+ int guard: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` holds. For the two operand form
+ * `guard ?: false` consider using `expr_cond_guard` instead.
+ */
+expr_cond_true(
+ unique int cond: @conditionalexpr ref,
+ int true: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` does not hold.
+ */
+expr_cond_false(
+ unique int cond: @conditionalexpr ref,
+ int false: @expr ref
+);
+
+/** A string representation of the value. */
+values(
+ unique int id: @value,
+ string str: string ref
+);
+
+/** The actual text in the source code for the value, if any. */
+valuetext(
+ unique int id: @value ref,
+ string text: string ref
+);
+
+valuebind(
+ int val: @value ref,
+ unique int expr: @expr ref
+);
+
+fieldoffsets(
+ unique int id: @variable ref,
+ int byteoffset: int ref,
+ int bitoffset: int ref
+);
+
+bitfield(
+ unique int id: @variable ref,
+ int bits: int ref,
+ int declared_bits: int ref
+);
+
+/* TODO
+memberprefix(
+ int member: @expr ref,
+ int prefix: @expr ref
+);
+*/
+
+/*
+ kind(1) = mbrcallexpr
+ kind(2) = mbrptrcallexpr
+ kind(3) = mbrptrmbrcallexpr
+ kind(4) = ptrmbrptrmbrcallexpr
+ kind(5) = mbrreadexpr // x.y
+ kind(6) = mbrptrreadexpr // p->y
+ kind(7) = mbrptrmbrreadexpr // x.*pm
+ kind(8) = mbrptrmbrptrreadexpr // x->*pm
+ kind(9) = staticmbrreadexpr // static x.y
+ kind(10) = staticmbrptrreadexpr // static p->y
+*/
+/* TODO
+memberaccess(
+ int member: @expr ref,
+ int kind: int ref
+);
+*/
+
+initialisers(
+ unique int init: @initialiser,
+ int var: @accessible ref,
+ unique int expr: @expr ref,
+ int location: @location_expr ref
+);
+
+braced_initialisers(
+ int init: @initialiser ref
+);
+
+/**
+ * An ancestor for the expression, for cases in which we cannot
+ * otherwise find the expression's parent.
+ */
+expr_ancestor(
+ int exp: @expr ref,
+ int ancestor: @element ref
+);
+
+exprs(
+ unique int id: @expr,
+ int kind: int ref,
+ int location: @location_expr ref
+);
+
+/*
+ case @value.category of
+ 1 = prval
+ | 2 = xval
+ | 3 = lval
+ ;
+*/
+expr_types(
+ int id: @expr ref,
+ int typeid: @type ref,
+ int value_category: int ref
+);
+
+case @expr.kind of
+ 1 = @errorexpr
+| 2 = @address_of // & AddressOfExpr
+| 3 = @reference_to // ReferenceToExpr (implicit?)
+| 4 = @indirect // * PointerDereferenceExpr
+| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?)
+// ...
+| 8 = @array_to_pointer // (???)
+| 9 = @vacuous_destructor_call // VacuousDestructorCall
+// ...
+| 11 = @assume // Microsoft
+| 12 = @parexpr
+| 13 = @arithnegexpr
+| 14 = @unaryplusexpr
+| 15 = @complementexpr
+| 16 = @notexpr
+| 17 = @conjugation // GNU ~ operator
+| 18 = @realpartexpr // GNU __real
+| 19 = @imagpartexpr // GNU __imag
+| 20 = @postincrexpr
+| 21 = @postdecrexpr
+| 22 = @preincrexpr
+| 23 = @predecrexpr
+| 24 = @conditionalexpr
+| 25 = @addexpr
+| 26 = @subexpr
+| 27 = @mulexpr
+| 28 = @divexpr
+| 29 = @remexpr
+| 30 = @jmulexpr // C99 mul imaginary
+| 31 = @jdivexpr // C99 div imaginary
+| 32 = @fjaddexpr // C99 add real + imaginary
+| 33 = @jfaddexpr // C99 add imaginary + real
+| 34 = @fjsubexpr // C99 sub real - imaginary
+| 35 = @jfsubexpr // C99 sub imaginary - real
+| 36 = @paddexpr // pointer add (pointer + int or int + pointer)
+| 37 = @psubexpr // pointer sub (pointer - integer)
+| 38 = @pdiffexpr // difference between two pointers
+| 39 = @lshiftexpr
+| 40 = @rshiftexpr
+| 41 = @andexpr
+| 42 = @orexpr
+| 43 = @xorexpr
+| 44 = @eqexpr
+| 45 = @neexpr
+| 46 = @gtexpr
+| 47 = @ltexpr
+| 48 = @geexpr
+| 49 = @leexpr
+| 50 = @minexpr // GNU minimum
+| 51 = @maxexpr // GNU maximum
+| 52 = @assignexpr
+| 53 = @assignaddexpr
+| 54 = @assignsubexpr
+| 55 = @assignmulexpr
+| 56 = @assigndivexpr
+| 57 = @assignremexpr
+| 58 = @assignlshiftexpr
+| 59 = @assignrshiftexpr
+| 60 = @assignandexpr
+| 61 = @assignorexpr
+| 62 = @assignxorexpr
+| 63 = @assignpaddexpr // assign pointer add
+| 64 = @assignpsubexpr // assign pointer sub
+| 65 = @andlogicalexpr
+| 66 = @orlogicalexpr
+| 67 = @commaexpr
+| 68 = @subscriptexpr // access to member of an array, e.g., a[5]
+// ... 69 @objc_subscriptexpr deprecated
+// ... 70 @cmdaccess deprecated
+// ...
+| 73 = @virtfunptrexpr
+| 74 = @callexpr
+// ... 75 @msgexpr_normal deprecated
+// ... 76 @msgexpr_super deprecated
+// ... 77 @atselectorexpr deprecated
+// ... 78 @atprotocolexpr deprecated
+| 79 = @vastartexpr
+| 80 = @vaargexpr
+| 81 = @vaendexpr
+| 82 = @vacopyexpr
+// ... 83 @atencodeexpr deprecated
+| 84 = @varaccess
+| 85 = @thisaccess
+// ... 86 @objc_box_expr deprecated
+| 87 = @new_expr
+| 88 = @delete_expr
+| 89 = @throw_expr
+| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2)
+| 91 = @braced_init_list
+| 92 = @type_id
+| 93 = @runtime_sizeof
+| 94 = @runtime_alignof
+| 95 = @sizeof_pack
+| 96 = @expr_stmt // GNU extension
+| 97 = @routineexpr
+| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....)
+| 99 = @offsetofexpr // offsetof ::= type and field
+| 100 = @hasassignexpr // __has_assign ::= type
+| 101 = @hascopyexpr // __has_copy ::= type
+| 102 = @hasnothrowassign // __has_nothrow_assign ::= type
+| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type
+| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type
+| 105 = @hastrivialassign // __has_trivial_assign ::= type
+| 106 = @hastrivialconstr // __has_trivial_constructor ::= type
+| 107 = @hastrivialcopy // __has_trivial_copy ::= type
+| 108 = @hasuserdestr // __has_user_destructor ::= type
+| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type
+| 110 = @isabstractexpr // __is_abstract ::= type
+| 111 = @isbaseofexpr // __is_base_of ::= type type
+| 112 = @isclassexpr // __is_class ::= type
+| 113 = @isconvtoexpr // __is_convertible_to ::= type type
+| 114 = @isemptyexpr // __is_empty ::= type
+| 115 = @isenumexpr // __is_enum ::= type
+| 116 = @ispodexpr // __is_pod ::= type
+| 117 = @ispolyexpr // __is_polymorphic ::= type
+| 118 = @isunionexpr // __is_union ::= type
+| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type
+| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof
+// ...
+| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type
+| 123 = @literal
+| 124 = @uuidof
+| 127 = @aggregateliteral
+| 128 = @delete_array_expr
+| 129 = @new_array_expr
+// ... 130 @objc_array_literal deprecated
+// ... 131 @objc_dictionary_literal deprecated
+| 132 = @foldexpr
+// ...
+| 200 = @ctordirectinit
+| 201 = @ctorvirtualinit
+| 202 = @ctorfieldinit
+| 203 = @ctordelegatinginit
+| 204 = @dtordirectdestruct
+| 205 = @dtorvirtualdestruct
+| 206 = @dtorfielddestruct
+// ...
+| 210 = @static_cast
+| 211 = @reinterpret_cast
+| 212 = @const_cast
+| 213 = @dynamic_cast
+| 214 = @c_style_cast
+| 215 = @lambdaexpr
+| 216 = @param_ref
+| 217 = @noopexpr
+// ...
+| 294 = @istriviallyconstructibleexpr
+| 295 = @isdestructibleexpr
+| 296 = @isnothrowdestructibleexpr
+| 297 = @istriviallydestructibleexpr
+| 298 = @istriviallyassignableexpr
+| 299 = @isnothrowassignableexpr
+| 300 = @istrivialexpr
+| 301 = @isstandardlayoutexpr
+| 302 = @istriviallycopyableexpr
+| 303 = @isliteraltypeexpr
+| 304 = @hastrivialmoveconstructorexpr
+| 305 = @hastrivialmoveassignexpr
+| 306 = @hasnothrowmoveassignexpr
+| 307 = @isconstructibleexpr
+| 308 = @isnothrowconstructibleexpr
+| 309 = @hasfinalizerexpr
+| 310 = @isdelegateexpr
+| 311 = @isinterfaceclassexpr
+| 312 = @isrefarrayexpr
+| 313 = @isrefclassexpr
+| 314 = @issealedexpr
+| 315 = @issimplevalueclassexpr
+| 316 = @isvalueclassexpr
+| 317 = @isfinalexpr
+| 319 = @noexceptexpr
+| 320 = @builtinshufflevector
+| 321 = @builtinchooseexpr
+| 322 = @builtinaddressof
+| 323 = @vec_fill
+| 324 = @builtinconvertvector
+| 325 = @builtincomplex
+| 326 = @spaceshipexpr
+| 327 = @co_await
+| 328 = @co_yield
+| 329 = @temp_init
+| 330 = @isassignable
+| 331 = @isaggregate
+| 332 = @hasuniqueobjectrepresentations
+| 333 = @builtinbitcast
+| 334 = @builtinshuffle
+;
+
+@var_args_expr = @vastartexpr
+ | @vaendexpr
+ | @vaargexpr
+ | @vacopyexpr
+ ;
+
+@builtin_op = @var_args_expr
+ | @noopexpr
+ | @offsetofexpr
+ | @intaddrexpr
+ | @hasassignexpr
+ | @hascopyexpr
+ | @hasnothrowassign
+ | @hasnothrowconstr
+ | @hasnothrowcopy
+ | @hastrivialassign
+ | @hastrivialconstr
+ | @hastrivialcopy
+ | @hastrivialdestructor
+ | @hasuserdestr
+ | @hasvirtualdestr
+ | @isabstractexpr
+ | @isbaseofexpr
+ | @isclassexpr
+ | @isconvtoexpr
+ | @isemptyexpr
+ | @isenumexpr
+ | @ispodexpr
+ | @ispolyexpr
+ | @isunionexpr
+ | @typescompexpr
+ | @builtinshufflevector
+ | @builtinconvertvector
+ | @builtinaddressof
+ | @istriviallyconstructibleexpr
+ | @isdestructibleexpr
+ | @isnothrowdestructibleexpr
+ | @istriviallydestructibleexpr
+ | @istriviallyassignableexpr
+ | @isnothrowassignableexpr
+ | @isstandardlayoutexpr
+ | @istriviallycopyableexpr
+ | @isliteraltypeexpr
+ | @hastrivialmoveconstructorexpr
+ | @hastrivialmoveassignexpr
+ | @hasnothrowmoveassignexpr
+ | @isconstructibleexpr
+ | @isnothrowconstructibleexpr
+ | @hasfinalizerexpr
+ | @isdelegateexpr
+ | @isinterfaceclassexpr
+ | @isrefarrayexpr
+ | @isrefclassexpr
+ | @issealedexpr
+ | @issimplevalueclassexpr
+ | @isvalueclassexpr
+ | @isfinalexpr
+ | @builtinchooseexpr
+ | @builtincomplex
+ | @isassignable
+ | @isaggregate
+ | @hasuniqueobjectrepresentations
+ | @builtinbitcast
+ | @builtinshuffle
+ ;
+
+new_allocated_type(
+ unique int expr: @new_expr ref,
+ int type_id: @type ref
+);
+
+new_array_allocated_type(
+ unique int expr: @new_array_expr ref,
+ int type_id: @type ref
+);
+
+/**
+ * The field being initialized by an initializer expression within an aggregate
+ * initializer for a class/struct/union.
+ */
+#keyset[aggregate, field]
+aggregate_field_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int field: @membervariable ref
+);
+
+/**
+ * The index of the element being initialized by an initializer expression
+ * within an aggregate initializer for an array.
+ */
+#keyset[aggregate, element_index]
+aggregate_array_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int element_index: int ref
+);
+
+@ctorinit = @ctordirectinit
+ | @ctorvirtualinit
+ | @ctorfieldinit
+ | @ctordelegatinginit;
+@dtordestruct = @dtordirectdestruct
+ | @dtorvirtualdestruct
+ | @dtorfielddestruct;
+
+
+condition_decl_bind(
+ unique int expr: @condition_decl ref,
+ unique int decl: @declaration ref
+);
+
+typeid_bind(
+ unique int expr: @type_id ref,
+ int type_id: @type ref
+);
+
+uuidof_bind(
+ unique int expr: @uuidof ref,
+ int type_id: @type ref
+);
+
+@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof;
+
+sizeof_bind(
+ unique int expr: @runtime_sizeof_or_alignof ref,
+ int type_id: @type ref
+);
+
+code_block(
+ unique int block: @literal ref,
+ unique int routine: @function ref
+);
+
+lambdas(
+ unique int expr: @lambdaexpr ref,
+ string default_capture: string ref,
+ boolean has_explicit_return_type: boolean ref
+);
+
+lambda_capture(
+ unique int id: @lambdacapture,
+ int lambda: @lambdaexpr ref,
+ int index: int ref,
+ int field: @membervariable ref,
+ boolean captured_by_reference: boolean ref,
+ boolean is_implicit: boolean ref,
+ int location: @location_default ref
+);
+
+@funbindexpr = @routineexpr
+ | @new_expr
+ | @delete_expr
+ | @delete_array_expr
+ | @ctordirectinit
+ | @ctorvirtualinit
+ | @ctordelegatinginit
+ | @dtordirectdestruct
+ | @dtorvirtualdestruct;
+
+@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct;
+@addressable = @function | @variable ;
+@accessible = @addressable | @enumconstant ;
+
+@access = @varaccess | @routineexpr ;
+
+fold(
+ int expr: @foldexpr ref,
+ string operator: string ref,
+ boolean is_left_fold: boolean ref
+);
+
+stmts(
+ unique int id: @stmt,
+ int kind: int ref,
+ int location: @location_stmt ref
+);
+
+case @stmt.kind of
+ 1 = @stmt_expr
+| 2 = @stmt_if
+| 3 = @stmt_while
+| 4 = @stmt_goto
+| 5 = @stmt_label
+| 6 = @stmt_return
+| 7 = @stmt_block
+| 8 = @stmt_end_test_while // do { ... } while ( ... )
+| 9 = @stmt_for
+| 10 = @stmt_switch_case
+| 11 = @stmt_switch
+| 13 = @stmt_asm // "asm" statement or the body of an asm function
+| 15 = @stmt_try_block
+| 16 = @stmt_microsoft_try // Microsoft
+| 17 = @stmt_decl
+| 18 = @stmt_set_vla_size // C99
+| 19 = @stmt_vla_decl // C99
+| 25 = @stmt_assigned_goto // GNU
+| 26 = @stmt_empty
+| 27 = @stmt_continue
+| 28 = @stmt_break
+| 29 = @stmt_range_based_for // C++11
+// ... 30 @stmt_at_autoreleasepool_block deprecated
+// ... 31 @stmt_objc_for_in deprecated
+// ... 32 @stmt_at_synchronized deprecated
+| 33 = @stmt_handler
+// ... 34 @stmt_finally_end deprecated
+| 35 = @stmt_constexpr_if
+| 37 = @stmt_co_return
+;
+
+type_vla(
+ int type_id: @type ref,
+ int decl: @stmt_vla_decl ref
+);
+
+variable_vla(
+ int var: @variable ref,
+ int decl: @stmt_vla_decl ref
+);
+
+if_initialization(
+ unique int if_stmt: @stmt_if ref,
+ int init_id: @stmt ref
+);
+
+if_then(
+ unique int if_stmt: @stmt_if ref,
+ int then_id: @stmt ref
+);
+
+if_else(
+ unique int if_stmt: @stmt_if ref,
+ int else_id: @stmt ref
+);
+
+constexpr_if_initialization(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int init_id: @stmt ref
+);
+
+constexpr_if_then(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int then_id: @stmt ref
+);
+
+constexpr_if_else(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int else_id: @stmt ref
+);
+
+while_body(
+ unique int while_stmt: @stmt_while ref,
+ int body_id: @stmt ref
+);
+
+do_body(
+ unique int do_stmt: @stmt_end_test_while ref,
+ int body_id: @stmt ref
+);
+
+switch_initialization(
+ unique int switch_stmt: @stmt_switch ref,
+ int init_id: @stmt ref
+);
+
+#keyset[switch_stmt, index]
+switch_case(
+ int switch_stmt: @stmt_switch ref,
+ int index: int ref,
+ int case_id: @stmt_switch_case ref
+);
+
+switch_body(
+ unique int switch_stmt: @stmt_switch ref,
+ int body_id: @stmt ref
+);
+
+for_initialization(
+ unique int for_stmt: @stmt_for ref,
+ int init_id: @stmt ref
+);
+
+for_condition(
+ unique int for_stmt: @stmt_for ref,
+ int condition_id: @expr ref
+);
+
+for_update(
+ unique int for_stmt: @stmt_for ref,
+ int update_id: @expr ref
+);
+
+for_body(
+ unique int for_stmt: @stmt_for ref,
+ int body_id: @stmt ref
+);
+
+@stmtparent = @stmt | @expr_stmt ;
+stmtparents(
+ unique int id: @stmt ref,
+ int index: int ref,
+ int parent: @stmtparent ref
+);
+
+ishandler(unique int block: @stmt_block ref);
+
+@cfgnode = @stmt | @expr | @function | @initialiser ;
+
+stmt_decl_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl: @declaration ref
+);
+
+stmt_decl_entry_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl_entry: @element ref
+);
+
+@functionorblock = @function | @stmt_block;
+
+blockscope(
+ unique int block: @stmt_block ref,
+ int enclosing: @functionorblock ref
+);
+
+@jump = @stmt_goto | @stmt_break | @stmt_continue;
+
+@jumporlabel = @jump | @stmt_label | @literal;
+
+jumpinfo(
+ unique int id: @jumporlabel ref,
+ string str: string ref,
+ int target: @stmt ref
+);
+
+preprocdirects(
+ unique int id: @preprocdirect,
+ int kind: int ref,
+ int location: @location_default ref
+);
+case @preprocdirect.kind of
+ 0 = @ppd_if
+| 1 = @ppd_ifdef
+| 2 = @ppd_ifndef
+| 3 = @ppd_elif
+| 4 = @ppd_else
+| 5 = @ppd_endif
+| 6 = @ppd_plain_include
+| 7 = @ppd_define
+| 8 = @ppd_undef
+| 9 = @ppd_line
+| 10 = @ppd_error
+| 11 = @ppd_pragma
+| 12 = @ppd_objc_import
+| 13 = @ppd_include_next
+| 18 = @ppd_warning
+;
+
+@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next;
+
+@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif;
+
+preprocpair(
+ int begin : @ppd_branch ref,
+ int elseelifend : @preprocdirect ref
+);
+
+preproctrue(int branch : @ppd_branch ref);
+preprocfalse(int branch : @ppd_branch ref);
+
+preproctext(
+ unique int id: @preprocdirect ref,
+ string head: string ref,
+ string body: string ref
+);
+
+includes(
+ unique int id: @ppd_include ref,
+ int included: @file ref
+);
+
+link_targets(
+ unique int id: @link_target,
+ int binary: @file ref
+);
+
+link_parent(
+ int element : @element ref,
+ int link_target : @link_target ref
+);
+
+/* XML Files */
+
+xmlEncoding(unique int id: @file ref, string encoding: string ref);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ string root: string ref,
+ string publicId: string ref,
+ string systemId: string ref,
+ int fileid: @file ref
+);
+
+xmlElements(
+ unique int id: @xmlelement,
+ string name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ string name: string ref,
+ string value: string ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlNs(
+ int id: @xmlnamespace,
+ string prefixName: string ref,
+ string URI: string ref,
+ int fileid: @file ref
+);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref
+);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref
+);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref
+);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref
+);
+
+@xmllocatable = @xmlcharacters
+ | @xmlelement
+ | @xmlcomment
+ | @xmlattribute
+ | @xmldtd
+ | @file
+ | @xmlnamespace;
diff --git a/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/semmlecode.cpp.dbscheme b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/semmlecode.cpp.dbscheme
new file mode 100644
index 00000000000..19e31bf071f
--- /dev/null
+++ b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/semmlecode.cpp.dbscheme
@@ -0,0 +1,2115 @@
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ /**
+ * An invocation of the compiler. Note that more than one file may
+ * be compiled per invocation. For example, this command compiles
+ * three source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ */
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | *path to extractor*
+ * 1 | `--mimic`
+ * 2 | `/usr/bin/gcc`
+ * 3 | `-c`
+ * 4 | f1.c
+ * 5 | f2.c
+ * 6 | f3.c
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.c
+ * 1 | f2.c
+ * 2 | f3.c
+ *
+ * Note that even if those files `#include` headers, those headers
+ * do not appear as rows.
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+
+/**
+ * External data, loaded from CSV files during snapshot creation. See
+ * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data)
+ * for more information.
+ */
+externalData(
+ int id : @externalDataElement,
+ string path : string ref,
+ int column: int ref,
+ string value : string ref
+);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Information about packages that provide code used during compilation.
+ * The `id` is just a unique identifier.
+ * The `namespace` is typically the name of the package manager that
+ * provided the package (e.g. "dpkg" or "yum").
+ * The `package_name` is the name of the package, and `version` is its
+ * version (as a string).
+ */
+external_packages(
+ unique int id: @external_package,
+ string namespace : string ref,
+ string package_name : string ref,
+ string version : string ref
+);
+
+/**
+ * Holds if File `fileid` was provided by package `package`.
+ */
+header_to_external_package(
+ int fileid : @file ref,
+ int package : @external_package ref
+);
+
+/*
+ * Version history
+ */
+
+svnentries(
+ unique int id : @svnentry,
+ string revision : string ref,
+ string author : string ref,
+ date revisionDate : date ref,
+ int changeSize : int ref
+)
+
+svnaffectedfiles(
+ int id : @svnentry ref,
+ int file : @file ref,
+ string action : string ref
+)
+
+svnentrymsg(
+ unique int id : @svnentry ref,
+ string message : string ref
+)
+
+svnchurn(
+ int commit : @svnentry ref,
+ int file : @file ref,
+ int addedLines : int ref,
+ int deletedLines : int ref
+)
+
+/*
+ * C++ dbscheme
+ */
+
+@location = @location_stmt | @location_expr | @location_default ;
+
+/**
+ * The location of an element that is not an expression or a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_default(
+ /** The location of an element that is not an expression or a statement. */
+ unique int id: @location_default,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_stmt(
+ /** The location of a statement. */
+ unique int id: @location_stmt,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of an expression.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_expr(
+ /** The location of an expression. */
+ unique int id: @location_expr,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/** An element for which line-count information is available. */
+@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location_default ref
+);
+
+files(
+ unique int id: @file,
+ string name: string ref
+);
+
+folders(
+ unique int id: @folder,
+ string name: string ref
+);
+
+@container = @folder | @file
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref
+);
+
+fileannotations(
+ int id: @file ref,
+ int kind: int ref,
+ string name: string ref,
+ string value: string ref
+);
+
+inmacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+affectedbymacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+/*
+ case @macroinvocations.kind of
+ 1 = macro expansion
+ | 2 = other macro reference
+ ;
+*/
+macroinvocations(
+ unique int id: @macroinvocation,
+ int macro_id: @ppd_define ref,
+ int location: @location_default ref,
+ int kind: int ref
+);
+
+macroparent(
+ unique int id: @macroinvocation ref,
+ int parent_id: @macroinvocation ref
+);
+
+// a macroinvocation may be part of another location
+// the way to find a constant expression that uses a macro
+// is thus to find a constant expression that has a location
+// to which a macro invocation is bound
+macrolocationbind(
+ int id: @macroinvocation ref,
+ int location: @location ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_unexpanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_expanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+/*
+ case @function.kind of
+ 1 = normal
+ | 2 = constructor
+ | 3 = destructor
+ | 4 = conversion
+ | 5 = operator
+ | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk
+ ;
+*/
+functions(
+ unique int id: @function,
+ string name: string ref,
+ int kind: int ref
+);
+
+function_entry_point(int id: @function ref, unique int entry_point: @stmt ref);
+
+function_return_type(int id: @function ref, int return_type: @type ref);
+
+/** If `function` is a coroutine, then this gives the
+ std::experimental::resumable_traits instance associated with it,
+ and the variables representing the `handle` and `promise` for it. */
+coroutine(
+ unique int function: @function ref,
+ int traits: @type ref,
+ int handle: @variable ref,
+ int promise: @variable ref
+);
+
+/** The `new` function used for allocating the coroutine state, if any. */
+coroutine_new(
+ unique int function: @function ref,
+ int new: @function ref
+);
+
+/** The `delete` function used for deallocating the coroutine state, if any. */
+coroutine_delete(
+ unique int function: @function ref,
+ int delete: @function ref
+);
+
+purefunctions(unique int id: @function ref);
+
+function_deleted(unique int id: @function ref);
+
+function_defaulted(unique int id: @function ref);
+
+member_function_this_type(unique int id: @function ref, int this_type: @type ref);
+
+#keyset[id, type_id]
+fun_decls(
+ int id: @fun_decl,
+ int function: @function ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+fun_def(unique int id: @fun_decl ref);
+fun_specialized(unique int id: @fun_decl ref);
+fun_implicit(unique int id: @fun_decl ref);
+fun_decl_specifiers(
+ int id: @fun_decl ref,
+ string name: string ref
+)
+#keyset[fun_decl, index]
+fun_decl_throws(
+ int fun_decl: @fun_decl ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+/* an empty throw specification is different from none */
+fun_decl_empty_throws(unique int fun_decl: @fun_decl ref);
+fun_decl_noexcept(
+ int fun_decl: @fun_decl ref,
+ int constant: @expr ref
+);
+fun_decl_empty_noexcept(int fun_decl: @fun_decl ref);
+fun_decl_typedef_type(
+ unique int fun_decl: @fun_decl ref,
+ int typedeftype_id: @usertype ref
+);
+
+param_decl_bind(
+ unique int id: @var_decl ref,
+ int index: int ref,
+ int fun_decl: @fun_decl ref
+);
+
+#keyset[id, type_id]
+var_decls(
+ int id: @var_decl,
+ int variable: @variable ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+var_def(unique int id: @var_decl ref);
+var_decl_specifiers(
+ int id: @var_decl ref,
+ string name: string ref
+)
+is_structured_binding(unique int id: @variable ref);
+
+type_decls(
+ unique int id: @type_decl,
+ int type_id: @type ref,
+ int location: @location_default ref
+);
+type_def(unique int id: @type_decl ref);
+type_decl_top(
+ unique int type_decl: @type_decl ref
+);
+
+namespace_decls(
+ unique int id: @namespace_decl,
+ int namespace_id: @namespace ref,
+ int location: @location_default ref,
+ int bodylocation: @location_default ref
+);
+
+usings(
+ unique int id: @using,
+ int element_id: @element ref,
+ int location: @location_default ref
+);
+
+/** The element which contains the `using` declaration. */
+using_container(
+ int parent: @element ref,
+ int child: @using ref
+);
+
+static_asserts(
+ unique int id: @static_assert,
+ int condition : @expr ref,
+ string message : string ref,
+ int location: @location_default ref,
+ int enclosing : @element ref
+);
+
+// each function has an ordered list of parameters
+#keyset[id, type_id]
+#keyset[function, index, type_id]
+params(
+ int id: @parameter,
+ int function: @functionorblock ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+overrides(int new: @function ref, int old: @function ref);
+
+#keyset[id, type_id]
+membervariables(
+ int id: @membervariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+globalvariables(
+ int id: @globalvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+localvariables(
+ int id: @localvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+autoderivation(
+ unique int var: @variable ref,
+ int derivation_type: @type ref
+);
+
+enumconstants(
+ unique int id: @enumconstant,
+ int parent: @usertype ref,
+ int index: int ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+
+@variable = @localscopevariable | @globalvariable | @membervariable;
+
+@localscopevariable = @localvariable | @parameter;
+
+/*
+ Built-in types are the fundamental types, e.g., integral, floating, and void.
+
+ case @builtintype.kind of
+ 1 = error
+ | 2 = unknown
+ | 3 = void
+ | 4 = boolean
+ | 5 = char
+ | 6 = unsigned_char
+ | 7 = signed_char
+ | 8 = short
+ | 9 = unsigned_short
+ | 10 = signed_short
+ | 11 = int
+ | 12 = unsigned_int
+ | 13 = signed_int
+ | 14 = long
+ | 15 = unsigned_long
+ | 16 = signed_long
+ | 17 = long_long
+ | 18 = unsigned_long_long
+ | 19 = signed_long_long
+ | 20 = __int8 // Microsoft-specific
+ | 21 = __int16 // Microsoft-specific
+ | 22 = __int32 // Microsoft-specific
+ | 23 = __int64 // Microsoft-specific
+ | 24 = float
+ | 25 = double
+ | 26 = long_double
+ | 27 = _Complex_float // C99-specific
+ | 28 = _Complex_double // C99-specific
+ | 29 = _Complex_long double // C99-specific
+ | 30 = _Imaginary_float // C99-specific
+ | 31 = _Imaginary_double // C99-specific
+ | 32 = _Imaginary_long_double // C99-specific
+ | 33 = wchar_t // Microsoft-specific
+ | 34 = decltype_nullptr // C++11
+ | 35 = __int128
+ | 36 = unsigned___int128
+ | 37 = signed___int128
+ | 38 = __float128
+ | 39 = _Complex___float128
+ | 40 = _Decimal32
+ | 41 = _Decimal64
+ | 42 = _Decimal128
+ | 43 = char16_t
+ | 44 = char32_t
+ | 45 = _Float32
+ | 46 = _Float32x
+ | 47 = _Float64
+ | 48 = _Float64x
+ | 49 = _Float128
+ | 50 = _Float128x
+ | 51 = char8_t
+ ;
+*/
+builtintypes(
+ unique int id: @builtintype,
+ string name: string ref,
+ int kind: int ref,
+ int size: int ref,
+ int sign: int ref,
+ int alignment: int ref
+);
+
+/*
+ Derived types are types that are directly derived from existing types and
+ point to, refer to, transform type data to return a new type.
+
+ case @derivedtype.kind of
+ 1 = pointer
+ | 2 = reference
+ | 3 = type_with_specifiers
+ | 4 = array
+ | 5 = gnu_vector
+ | 6 = routineptr
+ | 7 = routinereference
+ | 8 = rvalue_reference // C++11
+// ... 9 type_conforming_to_protocols deprecated
+ | 10 = block
+ ;
+*/
+derivedtypes(
+ unique int id: @derivedtype,
+ string name: string ref,
+ int kind: int ref,
+ int type_id: @type ref
+);
+
+pointerishsize(unique int id: @derivedtype ref,
+ int size: int ref,
+ int alignment: int ref);
+
+arraysizes(
+ unique int id: @derivedtype ref,
+ int num_elements: int ref,
+ int bytesize: int ref,
+ int alignment: int ref
+);
+
+typedefbase(
+ unique int id: @usertype ref,
+ int type_id: @type ref
+);
+
+/**
+ * An instance of the C++11 `decltype` operator. For example:
+ * ```
+ * int a;
+ * decltype(1+a) b;
+ * ```
+ * Here `expr` is `1+a`.
+ *
+ * Sometimes an additional pair of parentheses around the expression
+ * would change the semantics of this decltype, e.g.
+ * ```
+ * struct A { double x; };
+ * const A* a = new A();
+ * decltype( a->x ); // type is double
+ * decltype((a->x)); // type is const double&
+ * ```
+ * (Please consult the C++11 standard for more details).
+ * `parentheses_would_change_meaning` is `true` iff that is the case.
+ */
+#keyset[id, expr]
+decltypes(
+ int id: @decltype,
+ int expr: @expr ref,
+ int base_type: @type ref,
+ boolean parentheses_would_change_meaning: boolean ref
+);
+
+/*
+ case @usertype.kind of
+ 1 = struct
+ | 2 = class
+ | 3 = union
+ | 4 = enum
+ | 5 = typedef // classic C: typedef typedef type name
+ | 6 = template
+ | 7 = template_parameter
+ | 8 = template_template_parameter
+ | 9 = proxy_class // a proxy class associated with a template parameter
+// ... 10 objc_class deprecated
+// ... 11 objc_protocol deprecated
+// ... 12 objc_category deprecated
+ | 13 = scoped_enum
+ | 14 = using_alias // a using name = type style typedef
+ ;
+*/
+usertypes(
+ unique int id: @usertype,
+ string name: string ref,
+ int kind: int ref
+);
+
+usertypesize(
+ unique int id: @usertype ref,
+ int size: int ref,
+ int alignment: int ref
+);
+
+usertype_final(unique int id: @usertype ref);
+
+usertype_uuid(
+ unique int id: @usertype ref,
+ string uuid: string ref
+);
+
+mangled_name(
+ unique int id: @declaration ref,
+ int mangled_name : @mangledname
+);
+
+is_pod_class(unique int id: @usertype ref);
+is_standard_layout_class(unique int id: @usertype ref);
+
+is_complete(unique int id: @usertype ref);
+
+is_class_template(unique int id: @usertype ref);
+class_instantiation(
+ int to: @usertype ref,
+ int from: @usertype ref
+);
+class_template_argument(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+class_template_argument_value(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_proxy_class_for(
+ unique int id: @usertype ref,
+ unique int templ_param_id: @usertype ref
+);
+
+type_mentions(
+ unique int id: @type_mention,
+ int type_id: @type ref,
+ int location: @location ref,
+ // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there.
+ int kind: int ref
+);
+
+is_function_template(unique int id: @function ref);
+function_instantiation(
+ unique int to: @function ref,
+ int from: @function ref
+);
+function_template_argument(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+function_template_argument_value(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_variable_template(unique int id: @variable ref);
+variable_instantiation(
+ unique int to: @variable ref,
+ int from: @variable ref
+);
+variable_template_argument(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+variable_template_argument_value(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+/*
+ Fixed point types
+ precision(1) = short, precision(2) = default, precision(3) = long
+ is_unsigned(1) = unsigned is_unsigned(2) = signed
+ is_fract_type(1) = declared with _Fract
+ saturating(1) = declared with _Sat
+*/
+/* TODO
+fixedpointtypes(
+ unique int id: @fixedpointtype,
+ int precision: int ref,
+ int is_unsigned: int ref,
+ int is_fract_type: int ref,
+ int saturating: int ref);
+*/
+
+routinetypes(
+ unique int id: @routinetype,
+ int return_type: @type ref
+);
+
+routinetypeargs(
+ int routine: @routinetype ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+ptrtomembers(
+ unique int id: @ptrtomember,
+ int type_id: @type ref,
+ int class_id: @type ref
+);
+
+/*
+ specifiers for types, functions, and variables
+
+ "public",
+ "protected",
+ "private",
+
+ "const",
+ "volatile",
+ "static",
+
+ "pure",
+ "virtual",
+ "sealed", // Microsoft
+ "__interface", // Microsoft
+ "inline",
+ "explicit",
+
+ "near", // near far extension
+ "far", // near far extension
+ "__ptr32", // Microsoft
+ "__ptr64", // Microsoft
+ "__sptr", // Microsoft
+ "__uptr", // Microsoft
+ "dllimport", // Microsoft
+ "dllexport", // Microsoft
+ "thread", // Microsoft
+ "naked", // Microsoft
+ "microsoft_inline", // Microsoft
+ "forceinline", // Microsoft
+ "selectany", // Microsoft
+ "nothrow", // Microsoft
+ "novtable", // Microsoft
+ "noreturn", // Microsoft
+ "noinline", // Microsoft
+ "noalias", // Microsoft
+ "restrict", // Microsoft
+*/
+
+specifiers(
+ unique int id: @specifier,
+ unique string str: string ref
+);
+
+typespecifiers(
+ int type_id: @type ref,
+ int spec_id: @specifier ref
+);
+
+funspecifiers(
+ int func_id: @function ref,
+ int spec_id: @specifier ref
+);
+
+varspecifiers(
+ int var_id: @accessible ref,
+ int spec_id: @specifier ref
+);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ string name: string ref,
+ string name_space: string ref,
+ int location: @location_default ref
+);
+
+case @attribute.kind of
+ 0 = @gnuattribute
+| 1 = @stdattribute
+| 2 = @declspec
+| 3 = @msattribute
+| 4 = @alignas
+// ... 5 @objc_propertyattribute deprecated
+;
+
+attribute_args(
+ unique int id: @attribute_arg,
+ int kind: int ref,
+ int attribute: @attribute ref,
+ int index: int ref,
+ int location: @location_default ref
+);
+
+case @attribute_arg.kind of
+ 0 = @attribute_arg_empty
+| 1 = @attribute_arg_token
+| 2 = @attribute_arg_constant
+| 3 = @attribute_arg_type
+;
+
+attribute_arg_value(
+ unique int arg: @attribute_arg ref,
+ string value: string ref
+);
+attribute_arg_type(
+ unique int arg: @attribute_arg ref,
+ int type_id: @type ref
+);
+attribute_arg_name(
+ unique int arg: @attribute_arg ref,
+ string name: string ref
+);
+
+typeattributes(
+ int type_id: @type ref,
+ int spec_id: @attribute ref
+);
+
+funcattributes(
+ int func_id: @function ref,
+ int spec_id: @attribute ref
+);
+
+varattributes(
+ int var_id: @accessible ref,
+ int spec_id: @attribute ref
+);
+
+stmtattributes(
+ int stmt_id: @stmt ref,
+ int spec_id: @attribute ref
+);
+
+@type = @builtintype
+ | @derivedtype
+ | @usertype
+ /* TODO | @fixedpointtype */
+ | @routinetype
+ | @ptrtomember
+ | @decltype;
+
+unspecifiedtype(
+ unique int type_id: @type ref,
+ int unspecified_type_id: @type ref
+);
+
+member(
+ int parent: @type ref,
+ int index: int ref,
+ int child: @member ref
+);
+
+@enclosingfunction_child = @usertype | @variable | @namespace
+
+enclosingfunction(
+ unique int child: @enclosingfunction_child ref,
+ int parent: @function ref
+);
+
+derivations(
+ unique int derivation: @derivation,
+ int sub: @type ref,
+ int index: int ref,
+ int super: @type ref,
+ int location: @location_default ref
+);
+
+derspecifiers(
+ int der_id: @derivation ref,
+ int spec_id: @specifier ref
+);
+
+/**
+ * Contains the byte offset of the base class subobject within the derived
+ * class. Only holds for non-virtual base classes, but see table
+ * `virtual_base_offsets` for offsets of virtual base class subobjects.
+ */
+direct_base_offsets(
+ unique int der_id: @derivation ref,
+ int offset: int ref
+);
+
+/**
+ * Contains the byte offset of the virtual base class subobject for class
+ * `super` within a most-derived object of class `sub`. `super` can be either a
+ * direct or indirect base class.
+ */
+#keyset[sub, super]
+virtual_base_offsets(
+ int sub: @usertype ref,
+ int super: @usertype ref,
+ int offset: int ref
+);
+
+frienddecls(
+ unique int id: @frienddecl,
+ int type_id: @type ref,
+ int decl_id: @declaration ref,
+ int location: @location_default ref
+);
+
+@declaredtype = @usertype ;
+
+@declaration = @function
+ | @declaredtype
+ | @variable
+ | @enumconstant
+ | @frienddecl;
+
+@member = @membervariable
+ | @function
+ | @declaredtype
+ | @enumconstant;
+
+@locatable = @diagnostic
+ | @declaration
+ | @ppd_include
+ | @ppd_define
+ | @macroinvocation
+ /*| @funcall*/
+ | @xmllocatable
+ | @attribute
+ | @attribute_arg;
+
+@namedscope = @namespace | @usertype;
+
+@element = @locatable
+ | @file
+ | @folder
+ | @specifier
+ | @type
+ | @expr
+ | @namespace
+ | @initialiser
+ | @stmt
+ | @derivation
+ | @comment
+ | @preprocdirect
+ | @fun_decl
+ | @var_decl
+ | @type_decl
+ | @namespace_decl
+ | @using
+ | @namequalifier
+ | @specialnamequalifyingelement
+ | @static_assert
+ | @type_mention
+ | @lambdacapture;
+
+@exprparent = @element;
+
+comments(
+ unique int id: @comment,
+ string contents: string ref,
+ int location: @location_default ref
+);
+
+commentbinding(
+ int id: @comment ref,
+ int element: @element ref
+);
+
+exprconv(
+ int converted: @expr ref,
+ unique int conversion: @expr ref
+);
+
+compgenerated(unique int id: @element ref);
+
+/**
+ * `destructor_call` destructs the `i`'th entity that should be
+ * destructed following `element`. Note that entities should be
+ * destructed in reverse construction order, so for a given `element`
+ * these should be called from highest to lowest `i`.
+ */
+#keyset[element, destructor_call]
+#keyset[element, i]
+synthetic_destructor_call(
+ int element: @element ref,
+ int i: int ref,
+ int destructor_call: @routineexpr ref
+);
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref
+);
+
+namespace_inline(
+ unique int id: @namespace ref
+);
+
+namespacembrs(
+ int parentid: @namespace ref,
+ unique int memberid: @namespacembr ref
+);
+
+@namespacembr = @declaration | @namespace;
+
+exprparents(
+ int expr_id: @expr ref,
+ int child_index: int ref,
+ int parent_id: @exprparent ref
+);
+
+expr_isload(unique int expr_id: @expr ref);
+
+@cast = @c_style_cast
+ | @const_cast
+ | @dynamic_cast
+ | @reinterpret_cast
+ | @static_cast
+ ;
+
+/*
+case @conversion.kind of
+ 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast
+| 1 = @bool_conversion // conversion to 'bool'
+| 2 = @base_class_conversion // a derived-to-base conversion
+| 3 = @derived_class_conversion // a base-to-derived conversion
+| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member
+| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member
+| 6 = @glvalue_adjust // an adjustment of the type of a glvalue
+| 7 = @prvalue_adjust // an adjustment of the type of a prvalue
+;
+*/
+/**
+ * Describes the semantics represented by a cast expression. This is largely
+ * independent of the source syntax of the cast, so it is separate from the
+ * regular expression kind.
+ */
+conversionkinds(
+ unique int expr_id: @cast ref,
+ int kind: int ref
+);
+
+@conversion = @cast
+ | @array_to_pointer
+ | @parexpr
+ | @reference_to
+ | @ref_indirect
+ | @temp_init
+ ;
+
+/*
+case @funbindexpr.kind of
+ 0 = @normal_call // a normal call
+| 1 = @virtual_call // a virtual call
+| 2 = @adl_call // a call whose target is only found by ADL
+;
+*/
+iscall(unique int caller: @funbindexpr ref, int kind: int ref);
+
+numtemplatearguments(
+ unique int expr_id: @expr ref,
+ int num: int ref
+);
+
+specialnamequalifyingelements(
+ unique int id: @specialnamequalifyingelement,
+ unique string name: string ref
+);
+
+@namequalifiableelement = @expr | @namequalifier;
+@namequalifyingelement = @namespace
+ | @specialnamequalifyingelement
+ | @usertype;
+
+namequalifiers(
+ unique int id: @namequalifier,
+ unique int qualifiableelement: @namequalifiableelement ref,
+ int qualifyingelement: @namequalifyingelement ref,
+ int location: @location_default ref
+);
+
+varbind(
+ int expr: @varbindexpr ref,
+ int var: @accessible ref
+);
+
+funbind(
+ int expr: @funbindexpr ref,
+ int fun: @function ref
+);
+
+@any_new_expr = @new_expr
+ | @new_array_expr;
+
+@new_or_delete_expr = @any_new_expr
+ | @delete_expr
+ | @delete_array_expr;
+
+@prefix_crement_expr = @preincrexpr | @predecrexpr;
+
+@postfix_crement_expr = @postincrexpr | @postdecrexpr;
+
+@increment_expr = @preincrexpr | @postincrexpr;
+
+@decrement_expr = @predecrexpr | @postdecrexpr;
+
+@crement_expr = @increment_expr | @decrement_expr;
+
+@un_arith_op_expr = @arithnegexpr
+ | @unaryplusexpr
+ | @conjugation
+ | @realpartexpr
+ | @imagpartexpr
+ | @crement_expr
+ ;
+
+@un_bitwise_op_expr = @complementexpr;
+
+@un_log_op_expr = @notexpr;
+
+@un_op_expr = @address_of
+ | @indirect
+ | @un_arith_op_expr
+ | @un_bitwise_op_expr
+ | @builtinaddressof
+ | @vec_fill
+ | @un_log_op_expr
+ | @co_await
+ | @co_yield
+ ;
+
+@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr;
+
+@cmp_op_expr = @eq_op_expr | @rel_op_expr;
+
+@eq_op_expr = @eqexpr | @neexpr;
+
+@rel_op_expr = @gtexpr
+ | @ltexpr
+ | @geexpr
+ | @leexpr
+ | @spaceshipexpr
+ ;
+
+@bin_bitwise_op_expr = @lshiftexpr
+ | @rshiftexpr
+ | @andexpr
+ | @orexpr
+ | @xorexpr
+ ;
+
+@p_arith_op_expr = @paddexpr
+ | @psubexpr
+ | @pdiffexpr
+ ;
+
+@bin_arith_op_expr = @addexpr
+ | @subexpr
+ | @mulexpr
+ | @divexpr
+ | @remexpr
+ | @jmulexpr
+ | @jdivexpr
+ | @fjaddexpr
+ | @jfaddexpr
+ | @fjsubexpr
+ | @jfsubexpr
+ | @minexpr
+ | @maxexpr
+ | @p_arith_op_expr
+ ;
+
+@bin_op_expr = @bin_arith_op_expr
+ | @bin_bitwise_op_expr
+ | @cmp_op_expr
+ | @bin_log_op_expr
+ ;
+
+@op_expr = @un_op_expr
+ | @bin_op_expr
+ | @assign_expr
+ | @conditionalexpr
+ ;
+
+@assign_arith_expr = @assignaddexpr
+ | @assignsubexpr
+ | @assignmulexpr
+ | @assigndivexpr
+ | @assignremexpr
+ ;
+
+@assign_bitwise_expr = @assignandexpr
+ | @assignorexpr
+ | @assignxorexpr
+ | @assignlshiftexpr
+ | @assignrshiftexpr
+ | @assignpaddexpr
+ | @assignpsubexpr
+ ;
+
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr
+
+@assign_expr = @assignexpr | @assign_op_expr
+
+/*
+ case @allocator.form of
+ 0 = plain
+ | 1 = alignment
+ ;
+*/
+
+/**
+ * The allocator function associated with a `new` or `new[]` expression.
+ * The `form` column specified whether the allocation call contains an alignment
+ * argument.
+ */
+expr_allocator(
+ unique int expr: @any_new_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/*
+ case @deallocator.form of
+ 0 = plain
+ | 1 = size
+ | 2 = alignment
+ | 3 = size_and_alignment
+ ;
+*/
+
+/**
+ * The deallocator function associated with a `delete`, `delete[]`, `new`, or
+ * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the
+ * one used to free memory if the initialization throws an exception.
+ * The `form` column specifies whether the deallocation call contains a size
+ * argument, and alignment argument, or both.
+ */
+expr_deallocator(
+ unique int expr: @new_or_delete_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/**
+ * Holds if the `@conditionalexpr` is of the two operand form
+ * `guard ? : false`.
+ */
+expr_cond_two_operand(
+ unique int cond: @conditionalexpr ref
+);
+
+/**
+ * The guard of `@conditionalexpr` `guard ? true : false`
+ */
+expr_cond_guard(
+ unique int cond: @conditionalexpr ref,
+ int guard: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` holds. For the two operand form
+ * `guard ?: false` consider using `expr_cond_guard` instead.
+ */
+expr_cond_true(
+ unique int cond: @conditionalexpr ref,
+ int true: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` does not hold.
+ */
+expr_cond_false(
+ unique int cond: @conditionalexpr ref,
+ int false: @expr ref
+);
+
+/** A string representation of the value. */
+values(
+ unique int id: @value,
+ string str: string ref
+);
+
+/** The actual text in the source code for the value, if any. */
+valuetext(
+ unique int id: @value ref,
+ string text: string ref
+);
+
+valuebind(
+ int val: @value ref,
+ unique int expr: @expr ref
+);
+
+fieldoffsets(
+ unique int id: @variable ref,
+ int byteoffset: int ref,
+ int bitoffset: int ref
+);
+
+bitfield(
+ unique int id: @variable ref,
+ int bits: int ref,
+ int declared_bits: int ref
+);
+
+/* TODO
+memberprefix(
+ int member: @expr ref,
+ int prefix: @expr ref
+);
+*/
+
+/*
+ kind(1) = mbrcallexpr
+ kind(2) = mbrptrcallexpr
+ kind(3) = mbrptrmbrcallexpr
+ kind(4) = ptrmbrptrmbrcallexpr
+ kind(5) = mbrreadexpr // x.y
+ kind(6) = mbrptrreadexpr // p->y
+ kind(7) = mbrptrmbrreadexpr // x.*pm
+ kind(8) = mbrptrmbrptrreadexpr // x->*pm
+ kind(9) = staticmbrreadexpr // static x.y
+ kind(10) = staticmbrptrreadexpr // static p->y
+*/
+/* TODO
+memberaccess(
+ int member: @expr ref,
+ int kind: int ref
+);
+*/
+
+initialisers(
+ unique int init: @initialiser,
+ int var: @accessible ref,
+ unique int expr: @expr ref,
+ int location: @location_expr ref
+);
+
+braced_initialisers(
+ int init: @initialiser ref
+);
+
+/**
+ * An ancestor for the expression, for cases in which we cannot
+ * otherwise find the expression's parent.
+ */
+expr_ancestor(
+ int exp: @expr ref,
+ int ancestor: @element ref
+);
+
+exprs(
+ unique int id: @expr,
+ int kind: int ref,
+ int location: @location_expr ref
+);
+
+/*
+ case @value.category of
+ 1 = prval
+ | 2 = xval
+ | 3 = lval
+ ;
+*/
+expr_types(
+ int id: @expr ref,
+ int typeid: @type ref,
+ int value_category: int ref
+);
+
+case @expr.kind of
+ 1 = @errorexpr
+| 2 = @address_of // & AddressOfExpr
+| 3 = @reference_to // ReferenceToExpr (implicit?)
+| 4 = @indirect // * PointerDereferenceExpr
+| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?)
+// ...
+| 8 = @array_to_pointer // (???)
+| 9 = @vacuous_destructor_call // VacuousDestructorCall
+// ...
+| 11 = @assume // Microsoft
+| 12 = @parexpr
+| 13 = @arithnegexpr
+| 14 = @unaryplusexpr
+| 15 = @complementexpr
+| 16 = @notexpr
+| 17 = @conjugation // GNU ~ operator
+| 18 = @realpartexpr // GNU __real
+| 19 = @imagpartexpr // GNU __imag
+| 20 = @postincrexpr
+| 21 = @postdecrexpr
+| 22 = @preincrexpr
+| 23 = @predecrexpr
+| 24 = @conditionalexpr
+| 25 = @addexpr
+| 26 = @subexpr
+| 27 = @mulexpr
+| 28 = @divexpr
+| 29 = @remexpr
+| 30 = @jmulexpr // C99 mul imaginary
+| 31 = @jdivexpr // C99 div imaginary
+| 32 = @fjaddexpr // C99 add real + imaginary
+| 33 = @jfaddexpr // C99 add imaginary + real
+| 34 = @fjsubexpr // C99 sub real - imaginary
+| 35 = @jfsubexpr // C99 sub imaginary - real
+| 36 = @paddexpr // pointer add (pointer + int or int + pointer)
+| 37 = @psubexpr // pointer sub (pointer - integer)
+| 38 = @pdiffexpr // difference between two pointers
+| 39 = @lshiftexpr
+| 40 = @rshiftexpr
+| 41 = @andexpr
+| 42 = @orexpr
+| 43 = @xorexpr
+| 44 = @eqexpr
+| 45 = @neexpr
+| 46 = @gtexpr
+| 47 = @ltexpr
+| 48 = @geexpr
+| 49 = @leexpr
+| 50 = @minexpr // GNU minimum
+| 51 = @maxexpr // GNU maximum
+| 52 = @assignexpr
+| 53 = @assignaddexpr
+| 54 = @assignsubexpr
+| 55 = @assignmulexpr
+| 56 = @assigndivexpr
+| 57 = @assignremexpr
+| 58 = @assignlshiftexpr
+| 59 = @assignrshiftexpr
+| 60 = @assignandexpr
+| 61 = @assignorexpr
+| 62 = @assignxorexpr
+| 63 = @assignpaddexpr // assign pointer add
+| 64 = @assignpsubexpr // assign pointer sub
+| 65 = @andlogicalexpr
+| 66 = @orlogicalexpr
+| 67 = @commaexpr
+| 68 = @subscriptexpr // access to member of an array, e.g., a[5]
+// ... 69 @objc_subscriptexpr deprecated
+// ... 70 @cmdaccess deprecated
+// ...
+| 73 = @virtfunptrexpr
+| 74 = @callexpr
+// ... 75 @msgexpr_normal deprecated
+// ... 76 @msgexpr_super deprecated
+// ... 77 @atselectorexpr deprecated
+// ... 78 @atprotocolexpr deprecated
+| 79 = @vastartexpr
+| 80 = @vaargexpr
+| 81 = @vaendexpr
+| 82 = @vacopyexpr
+// ... 83 @atencodeexpr deprecated
+| 84 = @varaccess
+| 85 = @thisaccess
+// ... 86 @objc_box_expr deprecated
+| 87 = @new_expr
+| 88 = @delete_expr
+| 89 = @throw_expr
+| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2)
+| 91 = @braced_init_list
+| 92 = @type_id
+| 93 = @runtime_sizeof
+| 94 = @runtime_alignof
+| 95 = @sizeof_pack
+| 96 = @expr_stmt // GNU extension
+| 97 = @routineexpr
+| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....)
+| 99 = @offsetofexpr // offsetof ::= type and field
+| 100 = @hasassignexpr // __has_assign ::= type
+| 101 = @hascopyexpr // __has_copy ::= type
+| 102 = @hasnothrowassign // __has_nothrow_assign ::= type
+| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type
+| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type
+| 105 = @hastrivialassign // __has_trivial_assign ::= type
+| 106 = @hastrivialconstr // __has_trivial_constructor ::= type
+| 107 = @hastrivialcopy // __has_trivial_copy ::= type
+| 108 = @hasuserdestr // __has_user_destructor ::= type
+| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type
+| 110 = @isabstractexpr // __is_abstract ::= type
+| 111 = @isbaseofexpr // __is_base_of ::= type type
+| 112 = @isclassexpr // __is_class ::= type
+| 113 = @isconvtoexpr // __is_convertible_to ::= type type
+| 114 = @isemptyexpr // __is_empty ::= type
+| 115 = @isenumexpr // __is_enum ::= type
+| 116 = @ispodexpr // __is_pod ::= type
+| 117 = @ispolyexpr // __is_polymorphic ::= type
+| 118 = @isunionexpr // __is_union ::= type
+| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type
+| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof
+// ...
+| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type
+| 123 = @literal
+| 124 = @uuidof
+| 127 = @aggregateliteral
+| 128 = @delete_array_expr
+| 129 = @new_array_expr
+// ... 130 @objc_array_literal deprecated
+// ... 131 @objc_dictionary_literal deprecated
+| 132 = @foldexpr
+// ...
+| 200 = @ctordirectinit
+| 201 = @ctorvirtualinit
+| 202 = @ctorfieldinit
+| 203 = @ctordelegatinginit
+| 204 = @dtordirectdestruct
+| 205 = @dtorvirtualdestruct
+| 206 = @dtorfielddestruct
+// ...
+| 210 = @static_cast
+| 211 = @reinterpret_cast
+| 212 = @const_cast
+| 213 = @dynamic_cast
+| 214 = @c_style_cast
+| 215 = @lambdaexpr
+| 216 = @param_ref
+| 217 = @noopexpr
+// ...
+| 294 = @istriviallyconstructibleexpr
+| 295 = @isdestructibleexpr
+| 296 = @isnothrowdestructibleexpr
+| 297 = @istriviallydestructibleexpr
+| 298 = @istriviallyassignableexpr
+| 299 = @isnothrowassignableexpr
+| 300 = @istrivialexpr
+| 301 = @isstandardlayoutexpr
+| 302 = @istriviallycopyableexpr
+| 303 = @isliteraltypeexpr
+| 304 = @hastrivialmoveconstructorexpr
+| 305 = @hastrivialmoveassignexpr
+| 306 = @hasnothrowmoveassignexpr
+| 307 = @isconstructibleexpr
+| 308 = @isnothrowconstructibleexpr
+| 309 = @hasfinalizerexpr
+| 310 = @isdelegateexpr
+| 311 = @isinterfaceclassexpr
+| 312 = @isrefarrayexpr
+| 313 = @isrefclassexpr
+| 314 = @issealedexpr
+| 315 = @issimplevalueclassexpr
+| 316 = @isvalueclassexpr
+| 317 = @isfinalexpr
+| 319 = @noexceptexpr
+| 320 = @builtinshufflevector
+| 321 = @builtinchooseexpr
+| 322 = @builtinaddressof
+| 323 = @vec_fill
+| 324 = @builtinconvertvector
+| 325 = @builtincomplex
+| 326 = @spaceshipexpr
+| 327 = @co_await
+| 328 = @co_yield
+| 329 = @temp_init
+;
+
+@var_args_expr = @vastartexpr
+ | @vaendexpr
+ | @vaargexpr
+ | @vacopyexpr
+ ;
+
+@builtin_op = @var_args_expr
+ | @noopexpr
+ | @offsetofexpr
+ | @intaddrexpr
+ | @hasassignexpr
+ | @hascopyexpr
+ | @hasnothrowassign
+ | @hasnothrowconstr
+ | @hasnothrowcopy
+ | @hastrivialassign
+ | @hastrivialconstr
+ | @hastrivialcopy
+ | @hastrivialdestructor
+ | @hasuserdestr
+ | @hasvirtualdestr
+ | @isabstractexpr
+ | @isbaseofexpr
+ | @isclassexpr
+ | @isconvtoexpr
+ | @isemptyexpr
+ | @isenumexpr
+ | @ispodexpr
+ | @ispolyexpr
+ | @isunionexpr
+ | @typescompexpr
+ | @builtinshufflevector
+ | @builtinconvertvector
+ | @builtinaddressof
+ | @istriviallyconstructibleexpr
+ | @isdestructibleexpr
+ | @isnothrowdestructibleexpr
+ | @istriviallydestructibleexpr
+ | @istriviallyassignableexpr
+ | @isnothrowassignableexpr
+ | @isstandardlayoutexpr
+ | @istriviallycopyableexpr
+ | @isliteraltypeexpr
+ | @hastrivialmoveconstructorexpr
+ | @hastrivialmoveassignexpr
+ | @hasnothrowmoveassignexpr
+ | @isconstructibleexpr
+ | @isnothrowconstructibleexpr
+ | @hasfinalizerexpr
+ | @isdelegateexpr
+ | @isinterfaceclassexpr
+ | @isrefarrayexpr
+ | @isrefclassexpr
+ | @issealedexpr
+ | @issimplevalueclassexpr
+ | @isvalueclassexpr
+ | @isfinalexpr
+ | @builtinchooseexpr
+ | @builtincomplex
+ ;
+
+new_allocated_type(
+ unique int expr: @new_expr ref,
+ int type_id: @type ref
+);
+
+new_array_allocated_type(
+ unique int expr: @new_array_expr ref,
+ int type_id: @type ref
+);
+
+/**
+ * The field being initialized by an initializer expression within an aggregate
+ * initializer for a class/struct/union.
+ */
+#keyset[aggregate, field]
+aggregate_field_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int field: @membervariable ref
+);
+
+/**
+ * The index of the element being initialized by an initializer expression
+ * within an aggregate initializer for an array.
+ */
+#keyset[aggregate, element_index]
+aggregate_array_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int element_index: int ref
+);
+
+@ctorinit = @ctordirectinit
+ | @ctorvirtualinit
+ | @ctorfieldinit
+ | @ctordelegatinginit;
+@dtordestruct = @dtordirectdestruct
+ | @dtorvirtualdestruct
+ | @dtorfielddestruct;
+
+
+condition_decl_bind(
+ unique int expr: @condition_decl ref,
+ unique int decl: @declaration ref
+);
+
+typeid_bind(
+ unique int expr: @type_id ref,
+ int type_id: @type ref
+);
+
+uuidof_bind(
+ unique int expr: @uuidof ref,
+ int type_id: @type ref
+);
+
+@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof;
+
+sizeof_bind(
+ unique int expr: @runtime_sizeof_or_alignof ref,
+ int type_id: @type ref
+);
+
+code_block(
+ unique int block: @literal ref,
+ unique int routine: @function ref
+);
+
+lambdas(
+ unique int expr: @lambdaexpr ref,
+ string default_capture: string ref,
+ boolean has_explicit_return_type: boolean ref
+);
+
+lambda_capture(
+ unique int id: @lambdacapture,
+ int lambda: @lambdaexpr ref,
+ int index: int ref,
+ int field: @membervariable ref,
+ boolean captured_by_reference: boolean ref,
+ boolean is_implicit: boolean ref,
+ int location: @location_default ref
+);
+
+@funbindexpr = @routineexpr
+ | @new_expr
+ | @delete_expr
+ | @delete_array_expr
+ | @ctordirectinit
+ | @ctorvirtualinit
+ | @ctordelegatinginit
+ | @dtordirectdestruct
+ | @dtorvirtualdestruct;
+
+@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct;
+@addressable = @function | @variable ;
+@accessible = @addressable | @enumconstant ;
+
+@access = @varaccess | @routineexpr ;
+
+fold(
+ int expr: @foldexpr ref,
+ string operator: string ref,
+ boolean is_left_fold: boolean ref
+);
+
+stmts(
+ unique int id: @stmt,
+ int kind: int ref,
+ int location: @location_stmt ref
+);
+
+case @stmt.kind of
+ 1 = @stmt_expr
+| 2 = @stmt_if
+| 3 = @stmt_while
+| 4 = @stmt_goto
+| 5 = @stmt_label
+| 6 = @stmt_return
+| 7 = @stmt_block
+| 8 = @stmt_end_test_while // do { ... } while ( ... )
+| 9 = @stmt_for
+| 10 = @stmt_switch_case
+| 11 = @stmt_switch
+| 13 = @stmt_asm // "asm" statement or the body of an asm function
+| 15 = @stmt_try_block
+| 16 = @stmt_microsoft_try // Microsoft
+| 17 = @stmt_decl
+| 18 = @stmt_set_vla_size // C99
+| 19 = @stmt_vla_decl // C99
+| 25 = @stmt_assigned_goto // GNU
+| 26 = @stmt_empty
+| 27 = @stmt_continue
+| 28 = @stmt_break
+| 29 = @stmt_range_based_for // C++11
+// ... 30 @stmt_at_autoreleasepool_block deprecated
+// ... 31 @stmt_objc_for_in deprecated
+// ... 32 @stmt_at_synchronized deprecated
+| 33 = @stmt_handler
+// ... 34 @stmt_finally_end deprecated
+| 35 = @stmt_constexpr_if
+| 37 = @stmt_co_return
+;
+
+type_vla(
+ int type_id: @type ref,
+ int decl: @stmt_vla_decl ref
+);
+
+variable_vla(
+ int var: @variable ref,
+ int decl: @stmt_vla_decl ref
+);
+
+if_initialization(
+ unique int if_stmt: @stmt_if ref,
+ int init_id: @stmt ref
+);
+
+if_then(
+ unique int if_stmt: @stmt_if ref,
+ int then_id: @stmt ref
+);
+
+if_else(
+ unique int if_stmt: @stmt_if ref,
+ int else_id: @stmt ref
+);
+
+constexpr_if_initialization(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int init_id: @stmt ref
+);
+
+constexpr_if_then(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int then_id: @stmt ref
+);
+
+constexpr_if_else(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int else_id: @stmt ref
+);
+
+while_body(
+ unique int while_stmt: @stmt_while ref,
+ int body_id: @stmt ref
+);
+
+do_body(
+ unique int do_stmt: @stmt_end_test_while ref,
+ int body_id: @stmt ref
+);
+
+switch_initialization(
+ unique int switch_stmt: @stmt_switch ref,
+ int init_id: @stmt ref
+);
+
+#keyset[switch_stmt, index]
+switch_case(
+ int switch_stmt: @stmt_switch ref,
+ int index: int ref,
+ int case_id: @stmt_switch_case ref
+);
+
+switch_body(
+ unique int switch_stmt: @stmt_switch ref,
+ int body_id: @stmt ref
+);
+
+for_initialization(
+ unique int for_stmt: @stmt_for ref,
+ int init_id: @stmt ref
+);
+
+for_condition(
+ unique int for_stmt: @stmt_for ref,
+ int condition_id: @expr ref
+);
+
+for_update(
+ unique int for_stmt: @stmt_for ref,
+ int update_id: @expr ref
+);
+
+for_body(
+ unique int for_stmt: @stmt_for ref,
+ int body_id: @stmt ref
+);
+
+@stmtparent = @stmt | @expr_stmt ;
+stmtparents(
+ unique int id: @stmt ref,
+ int index: int ref,
+ int parent: @stmtparent ref
+);
+
+ishandler(unique int block: @stmt_block ref);
+
+@cfgnode = @stmt | @expr | @function | @initialiser ;
+
+stmt_decl_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl: @declaration ref
+);
+
+stmt_decl_entry_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl_entry: @element ref
+);
+
+@functionorblock = @function | @stmt_block;
+
+blockscope(
+ unique int block: @stmt_block ref,
+ int enclosing: @functionorblock ref
+);
+
+@jump = @stmt_goto | @stmt_break | @stmt_continue;
+
+@jumporlabel = @jump | @stmt_label | @literal;
+
+jumpinfo(
+ unique int id: @jumporlabel ref,
+ string str: string ref,
+ int target: @stmt ref
+);
+
+preprocdirects(
+ unique int id: @preprocdirect,
+ int kind: int ref,
+ int location: @location_default ref
+);
+case @preprocdirect.kind of
+ 0 = @ppd_if
+| 1 = @ppd_ifdef
+| 2 = @ppd_ifndef
+| 3 = @ppd_elif
+| 4 = @ppd_else
+| 5 = @ppd_endif
+| 6 = @ppd_plain_include
+| 7 = @ppd_define
+| 8 = @ppd_undef
+| 9 = @ppd_line
+| 10 = @ppd_error
+| 11 = @ppd_pragma
+| 12 = @ppd_objc_import
+| 13 = @ppd_include_next
+| 18 = @ppd_warning
+;
+
+@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next;
+
+@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif;
+
+preprocpair(
+ int begin : @ppd_branch ref,
+ int elseelifend : @preprocdirect ref
+);
+
+preproctrue(int branch : @ppd_branch ref);
+preprocfalse(int branch : @ppd_branch ref);
+
+preproctext(
+ unique int id: @preprocdirect ref,
+ string head: string ref,
+ string body: string ref
+);
+
+includes(
+ unique int id: @ppd_include ref,
+ int included: @file ref
+);
+
+link_targets(
+ unique int id: @link_target,
+ int binary: @file ref
+);
+
+link_parent(
+ int element : @element ref,
+ int link_target : @link_target ref
+);
+
+/* XML Files */
+
+xmlEncoding(unique int id: @file ref, string encoding: string ref);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ string root: string ref,
+ string publicId: string ref,
+ string systemId: string ref,
+ int fileid: @file ref
+);
+
+xmlElements(
+ unique int id: @xmlelement,
+ string name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ string name: string ref,
+ string value: string ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlNs(
+ int id: @xmlnamespace,
+ string prefixName: string ref,
+ string URI: string ref,
+ int fileid: @file ref
+);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref
+);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref
+);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref
+);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref
+);
+
+@xmllocatable = @xmlcharacters
+ | @xmlelement
+ | @xmlcomment
+ | @xmlattribute
+ | @xmldtd
+ | @file
+ | @xmlnamespace;
diff --git a/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/upgrade.properties b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/upgrade.properties
new file mode 100644
index 00000000000..d697a16a42f
--- /dev/null
+++ b/cpp/downgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/upgrade.properties
@@ -0,0 +1,3 @@
+description: Add new builtin operations
+compatibility: partial
+exprs.rel: run exprs.qlo
diff --git a/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/attribute_args.ql b/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/attribute_args.ql
new file mode 100644
index 00000000000..bc7bf989aac
--- /dev/null
+++ b/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/attribute_args.ql
@@ -0,0 +1,17 @@
+class AttributeArgument extends @attribute_arg {
+ string toString() { none() }
+}
+
+class Attribute extends @attribute {
+ string toString() { none() }
+}
+
+class LocationDefault extends @location_default {
+ string toString() { none() }
+}
+
+from AttributeArgument arg, int kind, Attribute attr, int index, LocationDefault location
+where
+ attribute_args(arg, kind, attr, index, location) and
+ not arg instanceof @attribute_arg_constant_expr
+select arg, kind, attr, index, location
diff --git a/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/old.dbscheme b/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/old.dbscheme
new file mode 100644
index 00000000000..34549c3b093
--- /dev/null
+++ b/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/old.dbscheme
@@ -0,0 +1,2130 @@
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ /**
+ * An invocation of the compiler. Note that more than one file may
+ * be compiled per invocation. For example, this command compiles
+ * three source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ */
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | *path to extractor*
+ * 1 | `--mimic`
+ * 2 | `/usr/bin/gcc`
+ * 3 | `-c`
+ * 4 | f1.c
+ * 5 | f2.c
+ * 6 | f3.c
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.c
+ * 1 | f2.c
+ * 2 | f3.c
+ *
+ * Note that even if those files `#include` headers, those headers
+ * do not appear as rows.
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+
+/**
+ * External data, loaded from CSV files during snapshot creation. See
+ * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data)
+ * for more information.
+ */
+externalData(
+ int id : @externalDataElement,
+ string path : string ref,
+ int column: int ref,
+ string value : string ref
+);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Information about packages that provide code used during compilation.
+ * The `id` is just a unique identifier.
+ * The `namespace` is typically the name of the package manager that
+ * provided the package (e.g. "dpkg" or "yum").
+ * The `package_name` is the name of the package, and `version` is its
+ * version (as a string).
+ */
+external_packages(
+ unique int id: @external_package,
+ string namespace : string ref,
+ string package_name : string ref,
+ string version : string ref
+);
+
+/**
+ * Holds if File `fileid` was provided by package `package`.
+ */
+header_to_external_package(
+ int fileid : @file ref,
+ int package : @external_package ref
+);
+
+/*
+ * Version history
+ */
+
+svnentries(
+ unique int id : @svnentry,
+ string revision : string ref,
+ string author : string ref,
+ date revisionDate : date ref,
+ int changeSize : int ref
+)
+
+svnaffectedfiles(
+ int id : @svnentry ref,
+ int file : @file ref,
+ string action : string ref
+)
+
+svnentrymsg(
+ unique int id : @svnentry ref,
+ string message : string ref
+)
+
+svnchurn(
+ int commit : @svnentry ref,
+ int file : @file ref,
+ int addedLines : int ref,
+ int deletedLines : int ref
+)
+
+/*
+ * C++ dbscheme
+ */
+
+@location = @location_stmt | @location_expr | @location_default ;
+
+/**
+ * The location of an element that is not an expression or a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_default(
+ /** The location of an element that is not an expression or a statement. */
+ unique int id: @location_default,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_stmt(
+ /** The location of a statement. */
+ unique int id: @location_stmt,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of an expression.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_expr(
+ /** The location of an expression. */
+ unique int id: @location_expr,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/** An element for which line-count information is available. */
+@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location_default ref
+);
+
+files(
+ unique int id: @file,
+ string name: string ref
+);
+
+folders(
+ unique int id: @folder,
+ string name: string ref
+);
+
+@container = @folder | @file
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref
+);
+
+fileannotations(
+ int id: @file ref,
+ int kind: int ref,
+ string name: string ref,
+ string value: string ref
+);
+
+inmacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+affectedbymacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+/*
+ case @macroinvocations.kind of
+ 1 = macro expansion
+ | 2 = other macro reference
+ ;
+*/
+macroinvocations(
+ unique int id: @macroinvocation,
+ int macro_id: @ppd_define ref,
+ int location: @location_default ref,
+ int kind: int ref
+);
+
+macroparent(
+ unique int id: @macroinvocation ref,
+ int parent_id: @macroinvocation ref
+);
+
+// a macroinvocation may be part of another location
+// the way to find a constant expression that uses a macro
+// is thus to find a constant expression that has a location
+// to which a macro invocation is bound
+macrolocationbind(
+ int id: @macroinvocation ref,
+ int location: @location ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_unexpanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_expanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+/*
+ case @function.kind of
+ 1 = normal
+ | 2 = constructor
+ | 3 = destructor
+ | 4 = conversion
+ | 5 = operator
+ | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk
+ ;
+*/
+functions(
+ unique int id: @function,
+ string name: string ref,
+ int kind: int ref
+);
+
+function_entry_point(int id: @function ref, unique int entry_point: @stmt ref);
+
+function_return_type(int id: @function ref, int return_type: @type ref);
+
+/** If `function` is a coroutine, then this gives the
+ std::experimental::resumable_traits instance associated with it,
+ and the variables representing the `handle` and `promise` for it. */
+coroutine(
+ unique int function: @function ref,
+ int traits: @type ref,
+ int handle: @variable ref,
+ int promise: @variable ref
+);
+
+/** The `new` function used for allocating the coroutine state, if any. */
+coroutine_new(
+ unique int function: @function ref,
+ int new: @function ref
+);
+
+/** The `delete` function used for deallocating the coroutine state, if any. */
+coroutine_delete(
+ unique int function: @function ref,
+ int delete: @function ref
+);
+
+purefunctions(unique int id: @function ref);
+
+function_deleted(unique int id: @function ref);
+
+function_defaulted(unique int id: @function ref);
+
+member_function_this_type(unique int id: @function ref, int this_type: @type ref);
+
+#keyset[id, type_id]
+fun_decls(
+ int id: @fun_decl,
+ int function: @function ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+fun_def(unique int id: @fun_decl ref);
+fun_specialized(unique int id: @fun_decl ref);
+fun_implicit(unique int id: @fun_decl ref);
+fun_decl_specifiers(
+ int id: @fun_decl ref,
+ string name: string ref
+)
+#keyset[fun_decl, index]
+fun_decl_throws(
+ int fun_decl: @fun_decl ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+/* an empty throw specification is different from none */
+fun_decl_empty_throws(unique int fun_decl: @fun_decl ref);
+fun_decl_noexcept(
+ int fun_decl: @fun_decl ref,
+ int constant: @expr ref
+);
+fun_decl_empty_noexcept(int fun_decl: @fun_decl ref);
+fun_decl_typedef_type(
+ unique int fun_decl: @fun_decl ref,
+ int typedeftype_id: @usertype ref
+);
+
+param_decl_bind(
+ unique int id: @var_decl ref,
+ int index: int ref,
+ int fun_decl: @fun_decl ref
+);
+
+#keyset[id, type_id]
+var_decls(
+ int id: @var_decl,
+ int variable: @variable ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+var_def(unique int id: @var_decl ref);
+var_decl_specifiers(
+ int id: @var_decl ref,
+ string name: string ref
+)
+is_structured_binding(unique int id: @variable ref);
+
+type_decls(
+ unique int id: @type_decl,
+ int type_id: @type ref,
+ int location: @location_default ref
+);
+type_def(unique int id: @type_decl ref);
+type_decl_top(
+ unique int type_decl: @type_decl ref
+);
+
+namespace_decls(
+ unique int id: @namespace_decl,
+ int namespace_id: @namespace ref,
+ int location: @location_default ref,
+ int bodylocation: @location_default ref
+);
+
+usings(
+ unique int id: @using,
+ int element_id: @element ref,
+ int location: @location_default ref
+);
+
+/** The element which contains the `using` declaration. */
+using_container(
+ int parent: @element ref,
+ int child: @using ref
+);
+
+static_asserts(
+ unique int id: @static_assert,
+ int condition : @expr ref,
+ string message : string ref,
+ int location: @location_default ref,
+ int enclosing : @element ref
+);
+
+// each function has an ordered list of parameters
+#keyset[id, type_id]
+#keyset[function, index, type_id]
+params(
+ int id: @parameter,
+ int function: @functionorblock ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+overrides(int new: @function ref, int old: @function ref);
+
+#keyset[id, type_id]
+membervariables(
+ int id: @membervariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+globalvariables(
+ int id: @globalvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+localvariables(
+ int id: @localvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+autoderivation(
+ unique int var: @variable ref,
+ int derivation_type: @type ref
+);
+
+enumconstants(
+ unique int id: @enumconstant,
+ int parent: @usertype ref,
+ int index: int ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+
+@variable = @localscopevariable | @globalvariable | @membervariable;
+
+@localscopevariable = @localvariable | @parameter;
+
+/*
+ Built-in types are the fundamental types, e.g., integral, floating, and void.
+
+ case @builtintype.kind of
+ 1 = error
+ | 2 = unknown
+ | 3 = void
+ | 4 = boolean
+ | 5 = char
+ | 6 = unsigned_char
+ | 7 = signed_char
+ | 8 = short
+ | 9 = unsigned_short
+ | 10 = signed_short
+ | 11 = int
+ | 12 = unsigned_int
+ | 13 = signed_int
+ | 14 = long
+ | 15 = unsigned_long
+ | 16 = signed_long
+ | 17 = long_long
+ | 18 = unsigned_long_long
+ | 19 = signed_long_long
+ | 20 = __int8 // Microsoft-specific
+ | 21 = __int16 // Microsoft-specific
+ | 22 = __int32 // Microsoft-specific
+ | 23 = __int64 // Microsoft-specific
+ | 24 = float
+ | 25 = double
+ | 26 = long_double
+ | 27 = _Complex_float // C99-specific
+ | 28 = _Complex_double // C99-specific
+ | 29 = _Complex_long double // C99-specific
+ | 30 = _Imaginary_float // C99-specific
+ | 31 = _Imaginary_double // C99-specific
+ | 32 = _Imaginary_long_double // C99-specific
+ | 33 = wchar_t // Microsoft-specific
+ | 34 = decltype_nullptr // C++11
+ | 35 = __int128
+ | 36 = unsigned___int128
+ | 37 = signed___int128
+ | 38 = __float128
+ | 39 = _Complex___float128
+ | 40 = _Decimal32
+ | 41 = _Decimal64
+ | 42 = _Decimal128
+ | 43 = char16_t
+ | 44 = char32_t
+ | 45 = _Float32
+ | 46 = _Float32x
+ | 47 = _Float64
+ | 48 = _Float64x
+ | 49 = _Float128
+ | 50 = _Float128x
+ | 51 = char8_t
+ ;
+*/
+builtintypes(
+ unique int id: @builtintype,
+ string name: string ref,
+ int kind: int ref,
+ int size: int ref,
+ int sign: int ref,
+ int alignment: int ref
+);
+
+/*
+ Derived types are types that are directly derived from existing types and
+ point to, refer to, transform type data to return a new type.
+
+ case @derivedtype.kind of
+ 1 = pointer
+ | 2 = reference
+ | 3 = type_with_specifiers
+ | 4 = array
+ | 5 = gnu_vector
+ | 6 = routineptr
+ | 7 = routinereference
+ | 8 = rvalue_reference // C++11
+// ... 9 type_conforming_to_protocols deprecated
+ | 10 = block
+ ;
+*/
+derivedtypes(
+ unique int id: @derivedtype,
+ string name: string ref,
+ int kind: int ref,
+ int type_id: @type ref
+);
+
+pointerishsize(unique int id: @derivedtype ref,
+ int size: int ref,
+ int alignment: int ref);
+
+arraysizes(
+ unique int id: @derivedtype ref,
+ int num_elements: int ref,
+ int bytesize: int ref,
+ int alignment: int ref
+);
+
+typedefbase(
+ unique int id: @usertype ref,
+ int type_id: @type ref
+);
+
+/**
+ * An instance of the C++11 `decltype` operator. For example:
+ * ```
+ * int a;
+ * decltype(1+a) b;
+ * ```
+ * Here `expr` is `1+a`.
+ *
+ * Sometimes an additional pair of parentheses around the expression
+ * would change the semantics of this decltype, e.g.
+ * ```
+ * struct A { double x; };
+ * const A* a = new A();
+ * decltype( a->x ); // type is double
+ * decltype((a->x)); // type is const double&
+ * ```
+ * (Please consult the C++11 standard for more details).
+ * `parentheses_would_change_meaning` is `true` iff that is the case.
+ */
+#keyset[id, expr]
+decltypes(
+ int id: @decltype,
+ int expr: @expr ref,
+ int base_type: @type ref,
+ boolean parentheses_would_change_meaning: boolean ref
+);
+
+/*
+ case @usertype.kind of
+ 1 = struct
+ | 2 = class
+ | 3 = union
+ | 4 = enum
+ | 5 = typedef // classic C: typedef typedef type name
+ | 6 = template
+ | 7 = template_parameter
+ | 8 = template_template_parameter
+ | 9 = proxy_class // a proxy class associated with a template parameter
+// ... 10 objc_class deprecated
+// ... 11 objc_protocol deprecated
+// ... 12 objc_category deprecated
+ | 13 = scoped_enum
+ | 14 = using_alias // a using name = type style typedef
+ ;
+*/
+usertypes(
+ unique int id: @usertype,
+ string name: string ref,
+ int kind: int ref
+);
+
+usertypesize(
+ unique int id: @usertype ref,
+ int size: int ref,
+ int alignment: int ref
+);
+
+usertype_final(unique int id: @usertype ref);
+
+usertype_uuid(
+ unique int id: @usertype ref,
+ string uuid: string ref
+);
+
+mangled_name(
+ unique int id: @declaration ref,
+ int mangled_name : @mangledname
+);
+
+is_pod_class(unique int id: @usertype ref);
+is_standard_layout_class(unique int id: @usertype ref);
+
+is_complete(unique int id: @usertype ref);
+
+is_class_template(unique int id: @usertype ref);
+class_instantiation(
+ int to: @usertype ref,
+ int from: @usertype ref
+);
+class_template_argument(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+class_template_argument_value(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_proxy_class_for(
+ unique int id: @usertype ref,
+ unique int templ_param_id: @usertype ref
+);
+
+type_mentions(
+ unique int id: @type_mention,
+ int type_id: @type ref,
+ int location: @location ref,
+ // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there.
+ int kind: int ref
+);
+
+is_function_template(unique int id: @function ref);
+function_instantiation(
+ unique int to: @function ref,
+ int from: @function ref
+);
+function_template_argument(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+function_template_argument_value(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_variable_template(unique int id: @variable ref);
+variable_instantiation(
+ unique int to: @variable ref,
+ int from: @variable ref
+);
+variable_template_argument(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+variable_template_argument_value(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+/*
+ Fixed point types
+ precision(1) = short, precision(2) = default, precision(3) = long
+ is_unsigned(1) = unsigned is_unsigned(2) = signed
+ is_fract_type(1) = declared with _Fract
+ saturating(1) = declared with _Sat
+*/
+/* TODO
+fixedpointtypes(
+ unique int id: @fixedpointtype,
+ int precision: int ref,
+ int is_unsigned: int ref,
+ int is_fract_type: int ref,
+ int saturating: int ref);
+*/
+
+routinetypes(
+ unique int id: @routinetype,
+ int return_type: @type ref
+);
+
+routinetypeargs(
+ int routine: @routinetype ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+ptrtomembers(
+ unique int id: @ptrtomember,
+ int type_id: @type ref,
+ int class_id: @type ref
+);
+
+/*
+ specifiers for types, functions, and variables
+
+ "public",
+ "protected",
+ "private",
+
+ "const",
+ "volatile",
+ "static",
+
+ "pure",
+ "virtual",
+ "sealed", // Microsoft
+ "__interface", // Microsoft
+ "inline",
+ "explicit",
+
+ "near", // near far extension
+ "far", // near far extension
+ "__ptr32", // Microsoft
+ "__ptr64", // Microsoft
+ "__sptr", // Microsoft
+ "__uptr", // Microsoft
+ "dllimport", // Microsoft
+ "dllexport", // Microsoft
+ "thread", // Microsoft
+ "naked", // Microsoft
+ "microsoft_inline", // Microsoft
+ "forceinline", // Microsoft
+ "selectany", // Microsoft
+ "nothrow", // Microsoft
+ "novtable", // Microsoft
+ "noreturn", // Microsoft
+ "noinline", // Microsoft
+ "noalias", // Microsoft
+ "restrict", // Microsoft
+*/
+
+specifiers(
+ unique int id: @specifier,
+ unique string str: string ref
+);
+
+typespecifiers(
+ int type_id: @type ref,
+ int spec_id: @specifier ref
+);
+
+funspecifiers(
+ int func_id: @function ref,
+ int spec_id: @specifier ref
+);
+
+varspecifiers(
+ int var_id: @accessible ref,
+ int spec_id: @specifier ref
+);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ string name: string ref,
+ string name_space: string ref,
+ int location: @location_default ref
+);
+
+case @attribute.kind of
+ 0 = @gnuattribute
+| 1 = @stdattribute
+| 2 = @declspec
+| 3 = @msattribute
+| 4 = @alignas
+// ... 5 @objc_propertyattribute deprecated
+;
+
+attribute_args(
+ unique int id: @attribute_arg,
+ int kind: int ref,
+ int attribute: @attribute ref,
+ int index: int ref,
+ int location: @location_default ref
+);
+
+case @attribute_arg.kind of
+ 0 = @attribute_arg_empty
+| 1 = @attribute_arg_token
+| 2 = @attribute_arg_constant
+| 3 = @attribute_arg_type
+| 4 = @attribute_arg_constant_expr
+;
+
+attribute_arg_value(
+ unique int arg: @attribute_arg ref,
+ string value: string ref
+);
+attribute_arg_type(
+ unique int arg: @attribute_arg ref,
+ int type_id: @type ref
+);
+attribute_arg_constant(
+ unique int arg: @attribute_arg ref,
+ int constant: @expr ref
+)
+attribute_arg_name(
+ unique int arg: @attribute_arg ref,
+ string name: string ref
+);
+
+typeattributes(
+ int type_id: @type ref,
+ int spec_id: @attribute ref
+);
+
+funcattributes(
+ int func_id: @function ref,
+ int spec_id: @attribute ref
+);
+
+varattributes(
+ int var_id: @accessible ref,
+ int spec_id: @attribute ref
+);
+
+stmtattributes(
+ int stmt_id: @stmt ref,
+ int spec_id: @attribute ref
+);
+
+@type = @builtintype
+ | @derivedtype
+ | @usertype
+ /* TODO | @fixedpointtype */
+ | @routinetype
+ | @ptrtomember
+ | @decltype;
+
+unspecifiedtype(
+ unique int type_id: @type ref,
+ int unspecified_type_id: @type ref
+);
+
+member(
+ int parent: @type ref,
+ int index: int ref,
+ int child: @member ref
+);
+
+@enclosingfunction_child = @usertype | @variable | @namespace
+
+enclosingfunction(
+ unique int child: @enclosingfunction_child ref,
+ int parent: @function ref
+);
+
+derivations(
+ unique int derivation: @derivation,
+ int sub: @type ref,
+ int index: int ref,
+ int super: @type ref,
+ int location: @location_default ref
+);
+
+derspecifiers(
+ int der_id: @derivation ref,
+ int spec_id: @specifier ref
+);
+
+/**
+ * Contains the byte offset of the base class subobject within the derived
+ * class. Only holds for non-virtual base classes, but see table
+ * `virtual_base_offsets` for offsets of virtual base class subobjects.
+ */
+direct_base_offsets(
+ unique int der_id: @derivation ref,
+ int offset: int ref
+);
+
+/**
+ * Contains the byte offset of the virtual base class subobject for class
+ * `super` within a most-derived object of class `sub`. `super` can be either a
+ * direct or indirect base class.
+ */
+#keyset[sub, super]
+virtual_base_offsets(
+ int sub: @usertype ref,
+ int super: @usertype ref,
+ int offset: int ref
+);
+
+frienddecls(
+ unique int id: @frienddecl,
+ int type_id: @type ref,
+ int decl_id: @declaration ref,
+ int location: @location_default ref
+);
+
+@declaredtype = @usertype ;
+
+@declaration = @function
+ | @declaredtype
+ | @variable
+ | @enumconstant
+ | @frienddecl;
+
+@member = @membervariable
+ | @function
+ | @declaredtype
+ | @enumconstant;
+
+@locatable = @diagnostic
+ | @declaration
+ | @ppd_include
+ | @ppd_define
+ | @macroinvocation
+ /*| @funcall*/
+ | @xmllocatable
+ | @attribute
+ | @attribute_arg;
+
+@namedscope = @namespace | @usertype;
+
+@element = @locatable
+ | @file
+ | @folder
+ | @specifier
+ | @type
+ | @expr
+ | @namespace
+ | @initialiser
+ | @stmt
+ | @derivation
+ | @comment
+ | @preprocdirect
+ | @fun_decl
+ | @var_decl
+ | @type_decl
+ | @namespace_decl
+ | @using
+ | @namequalifier
+ | @specialnamequalifyingelement
+ | @static_assert
+ | @type_mention
+ | @lambdacapture;
+
+@exprparent = @element;
+
+comments(
+ unique int id: @comment,
+ string contents: string ref,
+ int location: @location_default ref
+);
+
+commentbinding(
+ int id: @comment ref,
+ int element: @element ref
+);
+
+exprconv(
+ int converted: @expr ref,
+ unique int conversion: @expr ref
+);
+
+compgenerated(unique int id: @element ref);
+
+/**
+ * `destructor_call` destructs the `i`'th entity that should be
+ * destructed following `element`. Note that entities should be
+ * destructed in reverse construction order, so for a given `element`
+ * these should be called from highest to lowest `i`.
+ */
+#keyset[element, destructor_call]
+#keyset[element, i]
+synthetic_destructor_call(
+ int element: @element ref,
+ int i: int ref,
+ int destructor_call: @routineexpr ref
+);
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref
+);
+
+namespace_inline(
+ unique int id: @namespace ref
+);
+
+namespacembrs(
+ int parentid: @namespace ref,
+ unique int memberid: @namespacembr ref
+);
+
+@namespacembr = @declaration | @namespace;
+
+exprparents(
+ int expr_id: @expr ref,
+ int child_index: int ref,
+ int parent_id: @exprparent ref
+);
+
+expr_isload(unique int expr_id: @expr ref);
+
+@cast = @c_style_cast
+ | @const_cast
+ | @dynamic_cast
+ | @reinterpret_cast
+ | @static_cast
+ ;
+
+/*
+case @conversion.kind of
+ 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast
+| 1 = @bool_conversion // conversion to 'bool'
+| 2 = @base_class_conversion // a derived-to-base conversion
+| 3 = @derived_class_conversion // a base-to-derived conversion
+| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member
+| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member
+| 6 = @glvalue_adjust // an adjustment of the type of a glvalue
+| 7 = @prvalue_adjust // an adjustment of the type of a prvalue
+;
+*/
+/**
+ * Describes the semantics represented by a cast expression. This is largely
+ * independent of the source syntax of the cast, so it is separate from the
+ * regular expression kind.
+ */
+conversionkinds(
+ unique int expr_id: @cast ref,
+ int kind: int ref
+);
+
+@conversion = @cast
+ | @array_to_pointer
+ | @parexpr
+ | @reference_to
+ | @ref_indirect
+ | @temp_init
+ ;
+
+/*
+case @funbindexpr.kind of
+ 0 = @normal_call // a normal call
+| 1 = @virtual_call // a virtual call
+| 2 = @adl_call // a call whose target is only found by ADL
+;
+*/
+iscall(unique int caller: @funbindexpr ref, int kind: int ref);
+
+numtemplatearguments(
+ unique int expr_id: @expr ref,
+ int num: int ref
+);
+
+specialnamequalifyingelements(
+ unique int id: @specialnamequalifyingelement,
+ unique string name: string ref
+);
+
+@namequalifiableelement = @expr | @namequalifier;
+@namequalifyingelement = @namespace
+ | @specialnamequalifyingelement
+ | @usertype;
+
+namequalifiers(
+ unique int id: @namequalifier,
+ unique int qualifiableelement: @namequalifiableelement ref,
+ int qualifyingelement: @namequalifyingelement ref,
+ int location: @location_default ref
+);
+
+varbind(
+ int expr: @varbindexpr ref,
+ int var: @accessible ref
+);
+
+funbind(
+ int expr: @funbindexpr ref,
+ int fun: @function ref
+);
+
+@any_new_expr = @new_expr
+ | @new_array_expr;
+
+@new_or_delete_expr = @any_new_expr
+ | @delete_expr
+ | @delete_array_expr;
+
+@prefix_crement_expr = @preincrexpr | @predecrexpr;
+
+@postfix_crement_expr = @postincrexpr | @postdecrexpr;
+
+@increment_expr = @preincrexpr | @postincrexpr;
+
+@decrement_expr = @predecrexpr | @postdecrexpr;
+
+@crement_expr = @increment_expr | @decrement_expr;
+
+@un_arith_op_expr = @arithnegexpr
+ | @unaryplusexpr
+ | @conjugation
+ | @realpartexpr
+ | @imagpartexpr
+ | @crement_expr
+ ;
+
+@un_bitwise_op_expr = @complementexpr;
+
+@un_log_op_expr = @notexpr;
+
+@un_op_expr = @address_of
+ | @indirect
+ | @un_arith_op_expr
+ | @un_bitwise_op_expr
+ | @builtinaddressof
+ | @vec_fill
+ | @un_log_op_expr
+ | @co_await
+ | @co_yield
+ ;
+
+@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr;
+
+@cmp_op_expr = @eq_op_expr | @rel_op_expr;
+
+@eq_op_expr = @eqexpr | @neexpr;
+
+@rel_op_expr = @gtexpr
+ | @ltexpr
+ | @geexpr
+ | @leexpr
+ | @spaceshipexpr
+ ;
+
+@bin_bitwise_op_expr = @lshiftexpr
+ | @rshiftexpr
+ | @andexpr
+ | @orexpr
+ | @xorexpr
+ ;
+
+@p_arith_op_expr = @paddexpr
+ | @psubexpr
+ | @pdiffexpr
+ ;
+
+@bin_arith_op_expr = @addexpr
+ | @subexpr
+ | @mulexpr
+ | @divexpr
+ | @remexpr
+ | @jmulexpr
+ | @jdivexpr
+ | @fjaddexpr
+ | @jfaddexpr
+ | @fjsubexpr
+ | @jfsubexpr
+ | @minexpr
+ | @maxexpr
+ | @p_arith_op_expr
+ ;
+
+@bin_op_expr = @bin_arith_op_expr
+ | @bin_bitwise_op_expr
+ | @cmp_op_expr
+ | @bin_log_op_expr
+ ;
+
+@op_expr = @un_op_expr
+ | @bin_op_expr
+ | @assign_expr
+ | @conditionalexpr
+ ;
+
+@assign_arith_expr = @assignaddexpr
+ | @assignsubexpr
+ | @assignmulexpr
+ | @assigndivexpr
+ | @assignremexpr
+ ;
+
+@assign_bitwise_expr = @assignandexpr
+ | @assignorexpr
+ | @assignxorexpr
+ | @assignlshiftexpr
+ | @assignrshiftexpr
+ | @assignpaddexpr
+ | @assignpsubexpr
+ ;
+
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr
+
+@assign_expr = @assignexpr | @assign_op_expr
+
+/*
+ case @allocator.form of
+ 0 = plain
+ | 1 = alignment
+ ;
+*/
+
+/**
+ * The allocator function associated with a `new` or `new[]` expression.
+ * The `form` column specified whether the allocation call contains an alignment
+ * argument.
+ */
+expr_allocator(
+ unique int expr: @any_new_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/*
+ case @deallocator.form of
+ 0 = plain
+ | 1 = size
+ | 2 = alignment
+ | 3 = size_and_alignment
+ ;
+*/
+
+/**
+ * The deallocator function associated with a `delete`, `delete[]`, `new`, or
+ * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the
+ * one used to free memory if the initialization throws an exception.
+ * The `form` column specifies whether the deallocation call contains a size
+ * argument, and alignment argument, or both.
+ */
+expr_deallocator(
+ unique int expr: @new_or_delete_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/**
+ * Holds if the `@conditionalexpr` is of the two operand form
+ * `guard ? : false`.
+ */
+expr_cond_two_operand(
+ unique int cond: @conditionalexpr ref
+);
+
+/**
+ * The guard of `@conditionalexpr` `guard ? true : false`
+ */
+expr_cond_guard(
+ unique int cond: @conditionalexpr ref,
+ int guard: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` holds. For the two operand form
+ * `guard ?: false` consider using `expr_cond_guard` instead.
+ */
+expr_cond_true(
+ unique int cond: @conditionalexpr ref,
+ int true: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` does not hold.
+ */
+expr_cond_false(
+ unique int cond: @conditionalexpr ref,
+ int false: @expr ref
+);
+
+/** A string representation of the value. */
+values(
+ unique int id: @value,
+ string str: string ref
+);
+
+/** The actual text in the source code for the value, if any. */
+valuetext(
+ unique int id: @value ref,
+ string text: string ref
+);
+
+valuebind(
+ int val: @value ref,
+ unique int expr: @expr ref
+);
+
+fieldoffsets(
+ unique int id: @variable ref,
+ int byteoffset: int ref,
+ int bitoffset: int ref
+);
+
+bitfield(
+ unique int id: @variable ref,
+ int bits: int ref,
+ int declared_bits: int ref
+);
+
+/* TODO
+memberprefix(
+ int member: @expr ref,
+ int prefix: @expr ref
+);
+*/
+
+/*
+ kind(1) = mbrcallexpr
+ kind(2) = mbrptrcallexpr
+ kind(3) = mbrptrmbrcallexpr
+ kind(4) = ptrmbrptrmbrcallexpr
+ kind(5) = mbrreadexpr // x.y
+ kind(6) = mbrptrreadexpr // p->y
+ kind(7) = mbrptrmbrreadexpr // x.*pm
+ kind(8) = mbrptrmbrptrreadexpr // x->*pm
+ kind(9) = staticmbrreadexpr // static x.y
+ kind(10) = staticmbrptrreadexpr // static p->y
+*/
+/* TODO
+memberaccess(
+ int member: @expr ref,
+ int kind: int ref
+);
+*/
+
+initialisers(
+ unique int init: @initialiser,
+ int var: @accessible ref,
+ unique int expr: @expr ref,
+ int location: @location_expr ref
+);
+
+braced_initialisers(
+ int init: @initialiser ref
+);
+
+/**
+ * An ancestor for the expression, for cases in which we cannot
+ * otherwise find the expression's parent.
+ */
+expr_ancestor(
+ int exp: @expr ref,
+ int ancestor: @element ref
+);
+
+exprs(
+ unique int id: @expr,
+ int kind: int ref,
+ int location: @location_expr ref
+);
+
+/*
+ case @value.category of
+ 1 = prval
+ | 2 = xval
+ | 3 = lval
+ ;
+*/
+expr_types(
+ int id: @expr ref,
+ int typeid: @type ref,
+ int value_category: int ref
+);
+
+case @expr.kind of
+ 1 = @errorexpr
+| 2 = @address_of // & AddressOfExpr
+| 3 = @reference_to // ReferenceToExpr (implicit?)
+| 4 = @indirect // * PointerDereferenceExpr
+| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?)
+// ...
+| 8 = @array_to_pointer // (???)
+| 9 = @vacuous_destructor_call // VacuousDestructorCall
+// ...
+| 11 = @assume // Microsoft
+| 12 = @parexpr
+| 13 = @arithnegexpr
+| 14 = @unaryplusexpr
+| 15 = @complementexpr
+| 16 = @notexpr
+| 17 = @conjugation // GNU ~ operator
+| 18 = @realpartexpr // GNU __real
+| 19 = @imagpartexpr // GNU __imag
+| 20 = @postincrexpr
+| 21 = @postdecrexpr
+| 22 = @preincrexpr
+| 23 = @predecrexpr
+| 24 = @conditionalexpr
+| 25 = @addexpr
+| 26 = @subexpr
+| 27 = @mulexpr
+| 28 = @divexpr
+| 29 = @remexpr
+| 30 = @jmulexpr // C99 mul imaginary
+| 31 = @jdivexpr // C99 div imaginary
+| 32 = @fjaddexpr // C99 add real + imaginary
+| 33 = @jfaddexpr // C99 add imaginary + real
+| 34 = @fjsubexpr // C99 sub real - imaginary
+| 35 = @jfsubexpr // C99 sub imaginary - real
+| 36 = @paddexpr // pointer add (pointer + int or int + pointer)
+| 37 = @psubexpr // pointer sub (pointer - integer)
+| 38 = @pdiffexpr // difference between two pointers
+| 39 = @lshiftexpr
+| 40 = @rshiftexpr
+| 41 = @andexpr
+| 42 = @orexpr
+| 43 = @xorexpr
+| 44 = @eqexpr
+| 45 = @neexpr
+| 46 = @gtexpr
+| 47 = @ltexpr
+| 48 = @geexpr
+| 49 = @leexpr
+| 50 = @minexpr // GNU minimum
+| 51 = @maxexpr // GNU maximum
+| 52 = @assignexpr
+| 53 = @assignaddexpr
+| 54 = @assignsubexpr
+| 55 = @assignmulexpr
+| 56 = @assigndivexpr
+| 57 = @assignremexpr
+| 58 = @assignlshiftexpr
+| 59 = @assignrshiftexpr
+| 60 = @assignandexpr
+| 61 = @assignorexpr
+| 62 = @assignxorexpr
+| 63 = @assignpaddexpr // assign pointer add
+| 64 = @assignpsubexpr // assign pointer sub
+| 65 = @andlogicalexpr
+| 66 = @orlogicalexpr
+| 67 = @commaexpr
+| 68 = @subscriptexpr // access to member of an array, e.g., a[5]
+// ... 69 @objc_subscriptexpr deprecated
+// ... 70 @cmdaccess deprecated
+// ...
+| 73 = @virtfunptrexpr
+| 74 = @callexpr
+// ... 75 @msgexpr_normal deprecated
+// ... 76 @msgexpr_super deprecated
+// ... 77 @atselectorexpr deprecated
+// ... 78 @atprotocolexpr deprecated
+| 79 = @vastartexpr
+| 80 = @vaargexpr
+| 81 = @vaendexpr
+| 82 = @vacopyexpr
+// ... 83 @atencodeexpr deprecated
+| 84 = @varaccess
+| 85 = @thisaccess
+// ... 86 @objc_box_expr deprecated
+| 87 = @new_expr
+| 88 = @delete_expr
+| 89 = @throw_expr
+| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2)
+| 91 = @braced_init_list
+| 92 = @type_id
+| 93 = @runtime_sizeof
+| 94 = @runtime_alignof
+| 95 = @sizeof_pack
+| 96 = @expr_stmt // GNU extension
+| 97 = @routineexpr
+| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....)
+| 99 = @offsetofexpr // offsetof ::= type and field
+| 100 = @hasassignexpr // __has_assign ::= type
+| 101 = @hascopyexpr // __has_copy ::= type
+| 102 = @hasnothrowassign // __has_nothrow_assign ::= type
+| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type
+| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type
+| 105 = @hastrivialassign // __has_trivial_assign ::= type
+| 106 = @hastrivialconstr // __has_trivial_constructor ::= type
+| 107 = @hastrivialcopy // __has_trivial_copy ::= type
+| 108 = @hasuserdestr // __has_user_destructor ::= type
+| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type
+| 110 = @isabstractexpr // __is_abstract ::= type
+| 111 = @isbaseofexpr // __is_base_of ::= type type
+| 112 = @isclassexpr // __is_class ::= type
+| 113 = @isconvtoexpr // __is_convertible_to ::= type type
+| 114 = @isemptyexpr // __is_empty ::= type
+| 115 = @isenumexpr // __is_enum ::= type
+| 116 = @ispodexpr // __is_pod ::= type
+| 117 = @ispolyexpr // __is_polymorphic ::= type
+| 118 = @isunionexpr // __is_union ::= type
+| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type
+| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof
+// ...
+| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type
+| 123 = @literal
+| 124 = @uuidof
+| 127 = @aggregateliteral
+| 128 = @delete_array_expr
+| 129 = @new_array_expr
+// ... 130 @objc_array_literal deprecated
+// ... 131 @objc_dictionary_literal deprecated
+| 132 = @foldexpr
+// ...
+| 200 = @ctordirectinit
+| 201 = @ctorvirtualinit
+| 202 = @ctorfieldinit
+| 203 = @ctordelegatinginit
+| 204 = @dtordirectdestruct
+| 205 = @dtorvirtualdestruct
+| 206 = @dtorfielddestruct
+// ...
+| 210 = @static_cast
+| 211 = @reinterpret_cast
+| 212 = @const_cast
+| 213 = @dynamic_cast
+| 214 = @c_style_cast
+| 215 = @lambdaexpr
+| 216 = @param_ref
+| 217 = @noopexpr
+// ...
+| 294 = @istriviallyconstructibleexpr
+| 295 = @isdestructibleexpr
+| 296 = @isnothrowdestructibleexpr
+| 297 = @istriviallydestructibleexpr
+| 298 = @istriviallyassignableexpr
+| 299 = @isnothrowassignableexpr
+| 300 = @istrivialexpr
+| 301 = @isstandardlayoutexpr
+| 302 = @istriviallycopyableexpr
+| 303 = @isliteraltypeexpr
+| 304 = @hastrivialmoveconstructorexpr
+| 305 = @hastrivialmoveassignexpr
+| 306 = @hasnothrowmoveassignexpr
+| 307 = @isconstructibleexpr
+| 308 = @isnothrowconstructibleexpr
+| 309 = @hasfinalizerexpr
+| 310 = @isdelegateexpr
+| 311 = @isinterfaceclassexpr
+| 312 = @isrefarrayexpr
+| 313 = @isrefclassexpr
+| 314 = @issealedexpr
+| 315 = @issimplevalueclassexpr
+| 316 = @isvalueclassexpr
+| 317 = @isfinalexpr
+| 319 = @noexceptexpr
+| 320 = @builtinshufflevector
+| 321 = @builtinchooseexpr
+| 322 = @builtinaddressof
+| 323 = @vec_fill
+| 324 = @builtinconvertvector
+| 325 = @builtincomplex
+| 326 = @spaceshipexpr
+| 327 = @co_await
+| 328 = @co_yield
+| 329 = @temp_init
+| 330 = @isassignable
+| 331 = @isaggregate
+| 332 = @hasuniqueobjectrepresentations
+| 333 = @builtinbitcast
+| 334 = @builtinshuffle
+;
+
+@var_args_expr = @vastartexpr
+ | @vaendexpr
+ | @vaargexpr
+ | @vacopyexpr
+ ;
+
+@builtin_op = @var_args_expr
+ | @noopexpr
+ | @offsetofexpr
+ | @intaddrexpr
+ | @hasassignexpr
+ | @hascopyexpr
+ | @hasnothrowassign
+ | @hasnothrowconstr
+ | @hasnothrowcopy
+ | @hastrivialassign
+ | @hastrivialconstr
+ | @hastrivialcopy
+ | @hastrivialdestructor
+ | @hasuserdestr
+ | @hasvirtualdestr
+ | @isabstractexpr
+ | @isbaseofexpr
+ | @isclassexpr
+ | @isconvtoexpr
+ | @isemptyexpr
+ | @isenumexpr
+ | @ispodexpr
+ | @ispolyexpr
+ | @isunionexpr
+ | @typescompexpr
+ | @builtinshufflevector
+ | @builtinconvertvector
+ | @builtinaddressof
+ | @istriviallyconstructibleexpr
+ | @isdestructibleexpr
+ | @isnothrowdestructibleexpr
+ | @istriviallydestructibleexpr
+ | @istriviallyassignableexpr
+ | @isnothrowassignableexpr
+ | @isstandardlayoutexpr
+ | @istriviallycopyableexpr
+ | @isliteraltypeexpr
+ | @hastrivialmoveconstructorexpr
+ | @hastrivialmoveassignexpr
+ | @hasnothrowmoveassignexpr
+ | @isconstructibleexpr
+ | @isnothrowconstructibleexpr
+ | @hasfinalizerexpr
+ | @isdelegateexpr
+ | @isinterfaceclassexpr
+ | @isrefarrayexpr
+ | @isrefclassexpr
+ | @issealedexpr
+ | @issimplevalueclassexpr
+ | @isvalueclassexpr
+ | @isfinalexpr
+ | @builtinchooseexpr
+ | @builtincomplex
+ | @isassignable
+ | @isaggregate
+ | @hasuniqueobjectrepresentations
+ | @builtinbitcast
+ | @builtinshuffle
+ ;
+
+new_allocated_type(
+ unique int expr: @new_expr ref,
+ int type_id: @type ref
+);
+
+new_array_allocated_type(
+ unique int expr: @new_array_expr ref,
+ int type_id: @type ref
+);
+
+/**
+ * The field being initialized by an initializer expression within an aggregate
+ * initializer for a class/struct/union.
+ */
+#keyset[aggregate, field]
+aggregate_field_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int field: @membervariable ref
+);
+
+/**
+ * The index of the element being initialized by an initializer expression
+ * within an aggregate initializer for an array.
+ */
+#keyset[aggregate, element_index]
+aggregate_array_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int element_index: int ref
+);
+
+@ctorinit = @ctordirectinit
+ | @ctorvirtualinit
+ | @ctorfieldinit
+ | @ctordelegatinginit;
+@dtordestruct = @dtordirectdestruct
+ | @dtorvirtualdestruct
+ | @dtorfielddestruct;
+
+
+condition_decl_bind(
+ unique int expr: @condition_decl ref,
+ unique int decl: @declaration ref
+);
+
+typeid_bind(
+ unique int expr: @type_id ref,
+ int type_id: @type ref
+);
+
+uuidof_bind(
+ unique int expr: @uuidof ref,
+ int type_id: @type ref
+);
+
+@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof;
+
+sizeof_bind(
+ unique int expr: @runtime_sizeof_or_alignof ref,
+ int type_id: @type ref
+);
+
+code_block(
+ unique int block: @literal ref,
+ unique int routine: @function ref
+);
+
+lambdas(
+ unique int expr: @lambdaexpr ref,
+ string default_capture: string ref,
+ boolean has_explicit_return_type: boolean ref
+);
+
+lambda_capture(
+ unique int id: @lambdacapture,
+ int lambda: @lambdaexpr ref,
+ int index: int ref,
+ int field: @membervariable ref,
+ boolean captured_by_reference: boolean ref,
+ boolean is_implicit: boolean ref,
+ int location: @location_default ref
+);
+
+@funbindexpr = @routineexpr
+ | @new_expr
+ | @delete_expr
+ | @delete_array_expr
+ | @ctordirectinit
+ | @ctorvirtualinit
+ | @ctordelegatinginit
+ | @dtordirectdestruct
+ | @dtorvirtualdestruct;
+
+@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct;
+@addressable = @function | @variable ;
+@accessible = @addressable | @enumconstant ;
+
+@access = @varaccess | @routineexpr ;
+
+fold(
+ int expr: @foldexpr ref,
+ string operator: string ref,
+ boolean is_left_fold: boolean ref
+);
+
+stmts(
+ unique int id: @stmt,
+ int kind: int ref,
+ int location: @location_stmt ref
+);
+
+case @stmt.kind of
+ 1 = @stmt_expr
+| 2 = @stmt_if
+| 3 = @stmt_while
+| 4 = @stmt_goto
+| 5 = @stmt_label
+| 6 = @stmt_return
+| 7 = @stmt_block
+| 8 = @stmt_end_test_while // do { ... } while ( ... )
+| 9 = @stmt_for
+| 10 = @stmt_switch_case
+| 11 = @stmt_switch
+| 13 = @stmt_asm // "asm" statement or the body of an asm function
+| 15 = @stmt_try_block
+| 16 = @stmt_microsoft_try // Microsoft
+| 17 = @stmt_decl
+| 18 = @stmt_set_vla_size // C99
+| 19 = @stmt_vla_decl // C99
+| 25 = @stmt_assigned_goto // GNU
+| 26 = @stmt_empty
+| 27 = @stmt_continue
+| 28 = @stmt_break
+| 29 = @stmt_range_based_for // C++11
+// ... 30 @stmt_at_autoreleasepool_block deprecated
+// ... 31 @stmt_objc_for_in deprecated
+// ... 32 @stmt_at_synchronized deprecated
+| 33 = @stmt_handler
+// ... 34 @stmt_finally_end deprecated
+| 35 = @stmt_constexpr_if
+| 37 = @stmt_co_return
+;
+
+type_vla(
+ int type_id: @type ref,
+ int decl: @stmt_vla_decl ref
+);
+
+variable_vla(
+ int var: @variable ref,
+ int decl: @stmt_vla_decl ref
+);
+
+if_initialization(
+ unique int if_stmt: @stmt_if ref,
+ int init_id: @stmt ref
+);
+
+if_then(
+ unique int if_stmt: @stmt_if ref,
+ int then_id: @stmt ref
+);
+
+if_else(
+ unique int if_stmt: @stmt_if ref,
+ int else_id: @stmt ref
+);
+
+constexpr_if_initialization(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int init_id: @stmt ref
+);
+
+constexpr_if_then(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int then_id: @stmt ref
+);
+
+constexpr_if_else(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int else_id: @stmt ref
+);
+
+while_body(
+ unique int while_stmt: @stmt_while ref,
+ int body_id: @stmt ref
+);
+
+do_body(
+ unique int do_stmt: @stmt_end_test_while ref,
+ int body_id: @stmt ref
+);
+
+switch_initialization(
+ unique int switch_stmt: @stmt_switch ref,
+ int init_id: @stmt ref
+);
+
+#keyset[switch_stmt, index]
+switch_case(
+ int switch_stmt: @stmt_switch ref,
+ int index: int ref,
+ int case_id: @stmt_switch_case ref
+);
+
+switch_body(
+ unique int switch_stmt: @stmt_switch ref,
+ int body_id: @stmt ref
+);
+
+for_initialization(
+ unique int for_stmt: @stmt_for ref,
+ int init_id: @stmt ref
+);
+
+for_condition(
+ unique int for_stmt: @stmt_for ref,
+ int condition_id: @expr ref
+);
+
+for_update(
+ unique int for_stmt: @stmt_for ref,
+ int update_id: @expr ref
+);
+
+for_body(
+ unique int for_stmt: @stmt_for ref,
+ int body_id: @stmt ref
+);
+
+@stmtparent = @stmt | @expr_stmt ;
+stmtparents(
+ unique int id: @stmt ref,
+ int index: int ref,
+ int parent: @stmtparent ref
+);
+
+ishandler(unique int block: @stmt_block ref);
+
+@cfgnode = @stmt | @expr | @function | @initialiser ;
+
+stmt_decl_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl: @declaration ref
+);
+
+stmt_decl_entry_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl_entry: @element ref
+);
+
+@functionorblock = @function | @stmt_block;
+
+blockscope(
+ unique int block: @stmt_block ref,
+ int enclosing: @functionorblock ref
+);
+
+@jump = @stmt_goto | @stmt_break | @stmt_continue;
+
+@jumporlabel = @jump | @stmt_label | @literal;
+
+jumpinfo(
+ unique int id: @jumporlabel ref,
+ string str: string ref,
+ int target: @stmt ref
+);
+
+preprocdirects(
+ unique int id: @preprocdirect,
+ int kind: int ref,
+ int location: @location_default ref
+);
+case @preprocdirect.kind of
+ 0 = @ppd_if
+| 1 = @ppd_ifdef
+| 2 = @ppd_ifndef
+| 3 = @ppd_elif
+| 4 = @ppd_else
+| 5 = @ppd_endif
+| 6 = @ppd_plain_include
+| 7 = @ppd_define
+| 8 = @ppd_undef
+| 9 = @ppd_line
+| 10 = @ppd_error
+| 11 = @ppd_pragma
+| 12 = @ppd_objc_import
+| 13 = @ppd_include_next
+| 18 = @ppd_warning
+;
+
+@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next;
+
+@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif;
+
+preprocpair(
+ int begin : @ppd_branch ref,
+ int elseelifend : @preprocdirect ref
+);
+
+preproctrue(int branch : @ppd_branch ref);
+preprocfalse(int branch : @ppd_branch ref);
+
+preproctext(
+ unique int id: @preprocdirect ref,
+ string head: string ref,
+ string body: string ref
+);
+
+includes(
+ unique int id: @ppd_include ref,
+ int included: @file ref
+);
+
+link_targets(
+ unique int id: @link_target,
+ int binary: @file ref
+);
+
+link_parent(
+ int element : @element ref,
+ int link_target : @link_target ref
+);
+
+/* XML Files */
+
+xmlEncoding(unique int id: @file ref, string encoding: string ref);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ string root: string ref,
+ string publicId: string ref,
+ string systemId: string ref,
+ int fileid: @file ref
+);
+
+xmlElements(
+ unique int id: @xmlelement,
+ string name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ string name: string ref,
+ string value: string ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlNs(
+ int id: @xmlnamespace,
+ string prefixName: string ref,
+ string URI: string ref,
+ int fileid: @file ref
+);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref
+);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref
+);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref
+);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref
+);
+
+@xmllocatable = @xmlcharacters
+ | @xmlelement
+ | @xmlcomment
+ | @xmlattribute
+ | @xmldtd
+ | @file
+ | @xmlnamespace;
diff --git a/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/semmlecode.cpp.dbscheme b/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/semmlecode.cpp.dbscheme
new file mode 100644
index 00000000000..23f7cbb88a4
--- /dev/null
+++ b/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/semmlecode.cpp.dbscheme
@@ -0,0 +1,2125 @@
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ /**
+ * An invocation of the compiler. Note that more than one file may
+ * be compiled per invocation. For example, this command compiles
+ * three source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ */
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | *path to extractor*
+ * 1 | `--mimic`
+ * 2 | `/usr/bin/gcc`
+ * 3 | `-c`
+ * 4 | f1.c
+ * 5 | f2.c
+ * 6 | f3.c
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.c
+ * 1 | f2.c
+ * 2 | f3.c
+ *
+ * Note that even if those files `#include` headers, those headers
+ * do not appear as rows.
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+
+/**
+ * External data, loaded from CSV files during snapshot creation. See
+ * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data)
+ * for more information.
+ */
+externalData(
+ int id : @externalDataElement,
+ string path : string ref,
+ int column: int ref,
+ string value : string ref
+);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Information about packages that provide code used during compilation.
+ * The `id` is just a unique identifier.
+ * The `namespace` is typically the name of the package manager that
+ * provided the package (e.g. "dpkg" or "yum").
+ * The `package_name` is the name of the package, and `version` is its
+ * version (as a string).
+ */
+external_packages(
+ unique int id: @external_package,
+ string namespace : string ref,
+ string package_name : string ref,
+ string version : string ref
+);
+
+/**
+ * Holds if File `fileid` was provided by package `package`.
+ */
+header_to_external_package(
+ int fileid : @file ref,
+ int package : @external_package ref
+);
+
+/*
+ * Version history
+ */
+
+svnentries(
+ unique int id : @svnentry,
+ string revision : string ref,
+ string author : string ref,
+ date revisionDate : date ref,
+ int changeSize : int ref
+)
+
+svnaffectedfiles(
+ int id : @svnentry ref,
+ int file : @file ref,
+ string action : string ref
+)
+
+svnentrymsg(
+ unique int id : @svnentry ref,
+ string message : string ref
+)
+
+svnchurn(
+ int commit : @svnentry ref,
+ int file : @file ref,
+ int addedLines : int ref,
+ int deletedLines : int ref
+)
+
+/*
+ * C++ dbscheme
+ */
+
+@location = @location_stmt | @location_expr | @location_default ;
+
+/**
+ * The location of an element that is not an expression or a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_default(
+ /** The location of an element that is not an expression or a statement. */
+ unique int id: @location_default,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_stmt(
+ /** The location of a statement. */
+ unique int id: @location_stmt,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of an expression.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_expr(
+ /** The location of an expression. */
+ unique int id: @location_expr,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/** An element for which line-count information is available. */
+@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location_default ref
+);
+
+files(
+ unique int id: @file,
+ string name: string ref
+);
+
+folders(
+ unique int id: @folder,
+ string name: string ref
+);
+
+@container = @folder | @file
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref
+);
+
+fileannotations(
+ int id: @file ref,
+ int kind: int ref,
+ string name: string ref,
+ string value: string ref
+);
+
+inmacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+affectedbymacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+/*
+ case @macroinvocations.kind of
+ 1 = macro expansion
+ | 2 = other macro reference
+ ;
+*/
+macroinvocations(
+ unique int id: @macroinvocation,
+ int macro_id: @ppd_define ref,
+ int location: @location_default ref,
+ int kind: int ref
+);
+
+macroparent(
+ unique int id: @macroinvocation ref,
+ int parent_id: @macroinvocation ref
+);
+
+// a macroinvocation may be part of another location
+// the way to find a constant expression that uses a macro
+// is thus to find a constant expression that has a location
+// to which a macro invocation is bound
+macrolocationbind(
+ int id: @macroinvocation ref,
+ int location: @location ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_unexpanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_expanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+/*
+ case @function.kind of
+ 1 = normal
+ | 2 = constructor
+ | 3 = destructor
+ | 4 = conversion
+ | 5 = operator
+ | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk
+ ;
+*/
+functions(
+ unique int id: @function,
+ string name: string ref,
+ int kind: int ref
+);
+
+function_entry_point(int id: @function ref, unique int entry_point: @stmt ref);
+
+function_return_type(int id: @function ref, int return_type: @type ref);
+
+/** If `function` is a coroutine, then this gives the
+ std::experimental::resumable_traits instance associated with it,
+ and the variables representing the `handle` and `promise` for it. */
+coroutine(
+ unique int function: @function ref,
+ int traits: @type ref,
+ int handle: @variable ref,
+ int promise: @variable ref
+);
+
+/** The `new` function used for allocating the coroutine state, if any. */
+coroutine_new(
+ unique int function: @function ref,
+ int new: @function ref
+);
+
+/** The `delete` function used for deallocating the coroutine state, if any. */
+coroutine_delete(
+ unique int function: @function ref,
+ int delete: @function ref
+);
+
+purefunctions(unique int id: @function ref);
+
+function_deleted(unique int id: @function ref);
+
+function_defaulted(unique int id: @function ref);
+
+member_function_this_type(unique int id: @function ref, int this_type: @type ref);
+
+#keyset[id, type_id]
+fun_decls(
+ int id: @fun_decl,
+ int function: @function ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+fun_def(unique int id: @fun_decl ref);
+fun_specialized(unique int id: @fun_decl ref);
+fun_implicit(unique int id: @fun_decl ref);
+fun_decl_specifiers(
+ int id: @fun_decl ref,
+ string name: string ref
+)
+#keyset[fun_decl, index]
+fun_decl_throws(
+ int fun_decl: @fun_decl ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+/* an empty throw specification is different from none */
+fun_decl_empty_throws(unique int fun_decl: @fun_decl ref);
+fun_decl_noexcept(
+ int fun_decl: @fun_decl ref,
+ int constant: @expr ref
+);
+fun_decl_empty_noexcept(int fun_decl: @fun_decl ref);
+fun_decl_typedef_type(
+ unique int fun_decl: @fun_decl ref,
+ int typedeftype_id: @usertype ref
+);
+
+param_decl_bind(
+ unique int id: @var_decl ref,
+ int index: int ref,
+ int fun_decl: @fun_decl ref
+);
+
+#keyset[id, type_id]
+var_decls(
+ int id: @var_decl,
+ int variable: @variable ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+var_def(unique int id: @var_decl ref);
+var_decl_specifiers(
+ int id: @var_decl ref,
+ string name: string ref
+)
+is_structured_binding(unique int id: @variable ref);
+
+type_decls(
+ unique int id: @type_decl,
+ int type_id: @type ref,
+ int location: @location_default ref
+);
+type_def(unique int id: @type_decl ref);
+type_decl_top(
+ unique int type_decl: @type_decl ref
+);
+
+namespace_decls(
+ unique int id: @namespace_decl,
+ int namespace_id: @namespace ref,
+ int location: @location_default ref,
+ int bodylocation: @location_default ref
+);
+
+usings(
+ unique int id: @using,
+ int element_id: @element ref,
+ int location: @location_default ref
+);
+
+/** The element which contains the `using` declaration. */
+using_container(
+ int parent: @element ref,
+ int child: @using ref
+);
+
+static_asserts(
+ unique int id: @static_assert,
+ int condition : @expr ref,
+ string message : string ref,
+ int location: @location_default ref,
+ int enclosing : @element ref
+);
+
+// each function has an ordered list of parameters
+#keyset[id, type_id]
+#keyset[function, index, type_id]
+params(
+ int id: @parameter,
+ int function: @functionorblock ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+overrides(int new: @function ref, int old: @function ref);
+
+#keyset[id, type_id]
+membervariables(
+ int id: @membervariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+globalvariables(
+ int id: @globalvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+localvariables(
+ int id: @localvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+autoderivation(
+ unique int var: @variable ref,
+ int derivation_type: @type ref
+);
+
+enumconstants(
+ unique int id: @enumconstant,
+ int parent: @usertype ref,
+ int index: int ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+
+@variable = @localscopevariable | @globalvariable | @membervariable;
+
+@localscopevariable = @localvariable | @parameter;
+
+/*
+ Built-in types are the fundamental types, e.g., integral, floating, and void.
+
+ case @builtintype.kind of
+ 1 = error
+ | 2 = unknown
+ | 3 = void
+ | 4 = boolean
+ | 5 = char
+ | 6 = unsigned_char
+ | 7 = signed_char
+ | 8 = short
+ | 9 = unsigned_short
+ | 10 = signed_short
+ | 11 = int
+ | 12 = unsigned_int
+ | 13 = signed_int
+ | 14 = long
+ | 15 = unsigned_long
+ | 16 = signed_long
+ | 17 = long_long
+ | 18 = unsigned_long_long
+ | 19 = signed_long_long
+ | 20 = __int8 // Microsoft-specific
+ | 21 = __int16 // Microsoft-specific
+ | 22 = __int32 // Microsoft-specific
+ | 23 = __int64 // Microsoft-specific
+ | 24 = float
+ | 25 = double
+ | 26 = long_double
+ | 27 = _Complex_float // C99-specific
+ | 28 = _Complex_double // C99-specific
+ | 29 = _Complex_long double // C99-specific
+ | 30 = _Imaginary_float // C99-specific
+ | 31 = _Imaginary_double // C99-specific
+ | 32 = _Imaginary_long_double // C99-specific
+ | 33 = wchar_t // Microsoft-specific
+ | 34 = decltype_nullptr // C++11
+ | 35 = __int128
+ | 36 = unsigned___int128
+ | 37 = signed___int128
+ | 38 = __float128
+ | 39 = _Complex___float128
+ | 40 = _Decimal32
+ | 41 = _Decimal64
+ | 42 = _Decimal128
+ | 43 = char16_t
+ | 44 = char32_t
+ | 45 = _Float32
+ | 46 = _Float32x
+ | 47 = _Float64
+ | 48 = _Float64x
+ | 49 = _Float128
+ | 50 = _Float128x
+ | 51 = char8_t
+ ;
+*/
+builtintypes(
+ unique int id: @builtintype,
+ string name: string ref,
+ int kind: int ref,
+ int size: int ref,
+ int sign: int ref,
+ int alignment: int ref
+);
+
+/*
+ Derived types are types that are directly derived from existing types and
+ point to, refer to, transform type data to return a new type.
+
+ case @derivedtype.kind of
+ 1 = pointer
+ | 2 = reference
+ | 3 = type_with_specifiers
+ | 4 = array
+ | 5 = gnu_vector
+ | 6 = routineptr
+ | 7 = routinereference
+ | 8 = rvalue_reference // C++11
+// ... 9 type_conforming_to_protocols deprecated
+ | 10 = block
+ ;
+*/
+derivedtypes(
+ unique int id: @derivedtype,
+ string name: string ref,
+ int kind: int ref,
+ int type_id: @type ref
+);
+
+pointerishsize(unique int id: @derivedtype ref,
+ int size: int ref,
+ int alignment: int ref);
+
+arraysizes(
+ unique int id: @derivedtype ref,
+ int num_elements: int ref,
+ int bytesize: int ref,
+ int alignment: int ref
+);
+
+typedefbase(
+ unique int id: @usertype ref,
+ int type_id: @type ref
+);
+
+/**
+ * An instance of the C++11 `decltype` operator. For example:
+ * ```
+ * int a;
+ * decltype(1+a) b;
+ * ```
+ * Here `expr` is `1+a`.
+ *
+ * Sometimes an additional pair of parentheses around the expression
+ * would change the semantics of this decltype, e.g.
+ * ```
+ * struct A { double x; };
+ * const A* a = new A();
+ * decltype( a->x ); // type is double
+ * decltype((a->x)); // type is const double&
+ * ```
+ * (Please consult the C++11 standard for more details).
+ * `parentheses_would_change_meaning` is `true` iff that is the case.
+ */
+#keyset[id, expr]
+decltypes(
+ int id: @decltype,
+ int expr: @expr ref,
+ int base_type: @type ref,
+ boolean parentheses_would_change_meaning: boolean ref
+);
+
+/*
+ case @usertype.kind of
+ 1 = struct
+ | 2 = class
+ | 3 = union
+ | 4 = enum
+ | 5 = typedef // classic C: typedef typedef type name
+ | 6 = template
+ | 7 = template_parameter
+ | 8 = template_template_parameter
+ | 9 = proxy_class // a proxy class associated with a template parameter
+// ... 10 objc_class deprecated
+// ... 11 objc_protocol deprecated
+// ... 12 objc_category deprecated
+ | 13 = scoped_enum
+ | 14 = using_alias // a using name = type style typedef
+ ;
+*/
+usertypes(
+ unique int id: @usertype,
+ string name: string ref,
+ int kind: int ref
+);
+
+usertypesize(
+ unique int id: @usertype ref,
+ int size: int ref,
+ int alignment: int ref
+);
+
+usertype_final(unique int id: @usertype ref);
+
+usertype_uuid(
+ unique int id: @usertype ref,
+ string uuid: string ref
+);
+
+mangled_name(
+ unique int id: @declaration ref,
+ int mangled_name : @mangledname
+);
+
+is_pod_class(unique int id: @usertype ref);
+is_standard_layout_class(unique int id: @usertype ref);
+
+is_complete(unique int id: @usertype ref);
+
+is_class_template(unique int id: @usertype ref);
+class_instantiation(
+ int to: @usertype ref,
+ int from: @usertype ref
+);
+class_template_argument(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+class_template_argument_value(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_proxy_class_for(
+ unique int id: @usertype ref,
+ unique int templ_param_id: @usertype ref
+);
+
+type_mentions(
+ unique int id: @type_mention,
+ int type_id: @type ref,
+ int location: @location ref,
+ // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there.
+ int kind: int ref
+);
+
+is_function_template(unique int id: @function ref);
+function_instantiation(
+ unique int to: @function ref,
+ int from: @function ref
+);
+function_template_argument(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+function_template_argument_value(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_variable_template(unique int id: @variable ref);
+variable_instantiation(
+ unique int to: @variable ref,
+ int from: @variable ref
+);
+variable_template_argument(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+variable_template_argument_value(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+/*
+ Fixed point types
+ precision(1) = short, precision(2) = default, precision(3) = long
+ is_unsigned(1) = unsigned is_unsigned(2) = signed
+ is_fract_type(1) = declared with _Fract
+ saturating(1) = declared with _Sat
+*/
+/* TODO
+fixedpointtypes(
+ unique int id: @fixedpointtype,
+ int precision: int ref,
+ int is_unsigned: int ref,
+ int is_fract_type: int ref,
+ int saturating: int ref);
+*/
+
+routinetypes(
+ unique int id: @routinetype,
+ int return_type: @type ref
+);
+
+routinetypeargs(
+ int routine: @routinetype ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+ptrtomembers(
+ unique int id: @ptrtomember,
+ int type_id: @type ref,
+ int class_id: @type ref
+);
+
+/*
+ specifiers for types, functions, and variables
+
+ "public",
+ "protected",
+ "private",
+
+ "const",
+ "volatile",
+ "static",
+
+ "pure",
+ "virtual",
+ "sealed", // Microsoft
+ "__interface", // Microsoft
+ "inline",
+ "explicit",
+
+ "near", // near far extension
+ "far", // near far extension
+ "__ptr32", // Microsoft
+ "__ptr64", // Microsoft
+ "__sptr", // Microsoft
+ "__uptr", // Microsoft
+ "dllimport", // Microsoft
+ "dllexport", // Microsoft
+ "thread", // Microsoft
+ "naked", // Microsoft
+ "microsoft_inline", // Microsoft
+ "forceinline", // Microsoft
+ "selectany", // Microsoft
+ "nothrow", // Microsoft
+ "novtable", // Microsoft
+ "noreturn", // Microsoft
+ "noinline", // Microsoft
+ "noalias", // Microsoft
+ "restrict", // Microsoft
+*/
+
+specifiers(
+ unique int id: @specifier,
+ unique string str: string ref
+);
+
+typespecifiers(
+ int type_id: @type ref,
+ int spec_id: @specifier ref
+);
+
+funspecifiers(
+ int func_id: @function ref,
+ int spec_id: @specifier ref
+);
+
+varspecifiers(
+ int var_id: @accessible ref,
+ int spec_id: @specifier ref
+);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ string name: string ref,
+ string name_space: string ref,
+ int location: @location_default ref
+);
+
+case @attribute.kind of
+ 0 = @gnuattribute
+| 1 = @stdattribute
+| 2 = @declspec
+| 3 = @msattribute
+| 4 = @alignas
+// ... 5 @objc_propertyattribute deprecated
+;
+
+attribute_args(
+ unique int id: @attribute_arg,
+ int kind: int ref,
+ int attribute: @attribute ref,
+ int index: int ref,
+ int location: @location_default ref
+);
+
+case @attribute_arg.kind of
+ 0 = @attribute_arg_empty
+| 1 = @attribute_arg_token
+| 2 = @attribute_arg_constant
+| 3 = @attribute_arg_type
+;
+
+attribute_arg_value(
+ unique int arg: @attribute_arg ref,
+ string value: string ref
+);
+attribute_arg_type(
+ unique int arg: @attribute_arg ref,
+ int type_id: @type ref
+);
+attribute_arg_name(
+ unique int arg: @attribute_arg ref,
+ string name: string ref
+);
+
+typeattributes(
+ int type_id: @type ref,
+ int spec_id: @attribute ref
+);
+
+funcattributes(
+ int func_id: @function ref,
+ int spec_id: @attribute ref
+);
+
+varattributes(
+ int var_id: @accessible ref,
+ int spec_id: @attribute ref
+);
+
+stmtattributes(
+ int stmt_id: @stmt ref,
+ int spec_id: @attribute ref
+);
+
+@type = @builtintype
+ | @derivedtype
+ | @usertype
+ /* TODO | @fixedpointtype */
+ | @routinetype
+ | @ptrtomember
+ | @decltype;
+
+unspecifiedtype(
+ unique int type_id: @type ref,
+ int unspecified_type_id: @type ref
+);
+
+member(
+ int parent: @type ref,
+ int index: int ref,
+ int child: @member ref
+);
+
+@enclosingfunction_child = @usertype | @variable | @namespace
+
+enclosingfunction(
+ unique int child: @enclosingfunction_child ref,
+ int parent: @function ref
+);
+
+derivations(
+ unique int derivation: @derivation,
+ int sub: @type ref,
+ int index: int ref,
+ int super: @type ref,
+ int location: @location_default ref
+);
+
+derspecifiers(
+ int der_id: @derivation ref,
+ int spec_id: @specifier ref
+);
+
+/**
+ * Contains the byte offset of the base class subobject within the derived
+ * class. Only holds for non-virtual base classes, but see table
+ * `virtual_base_offsets` for offsets of virtual base class subobjects.
+ */
+direct_base_offsets(
+ unique int der_id: @derivation ref,
+ int offset: int ref
+);
+
+/**
+ * Contains the byte offset of the virtual base class subobject for class
+ * `super` within a most-derived object of class `sub`. `super` can be either a
+ * direct or indirect base class.
+ */
+#keyset[sub, super]
+virtual_base_offsets(
+ int sub: @usertype ref,
+ int super: @usertype ref,
+ int offset: int ref
+);
+
+frienddecls(
+ unique int id: @frienddecl,
+ int type_id: @type ref,
+ int decl_id: @declaration ref,
+ int location: @location_default ref
+);
+
+@declaredtype = @usertype ;
+
+@declaration = @function
+ | @declaredtype
+ | @variable
+ | @enumconstant
+ | @frienddecl;
+
+@member = @membervariable
+ | @function
+ | @declaredtype
+ | @enumconstant;
+
+@locatable = @diagnostic
+ | @declaration
+ | @ppd_include
+ | @ppd_define
+ | @macroinvocation
+ /*| @funcall*/
+ | @xmllocatable
+ | @attribute
+ | @attribute_arg;
+
+@namedscope = @namespace | @usertype;
+
+@element = @locatable
+ | @file
+ | @folder
+ | @specifier
+ | @type
+ | @expr
+ | @namespace
+ | @initialiser
+ | @stmt
+ | @derivation
+ | @comment
+ | @preprocdirect
+ | @fun_decl
+ | @var_decl
+ | @type_decl
+ | @namespace_decl
+ | @using
+ | @namequalifier
+ | @specialnamequalifyingelement
+ | @static_assert
+ | @type_mention
+ | @lambdacapture;
+
+@exprparent = @element;
+
+comments(
+ unique int id: @comment,
+ string contents: string ref,
+ int location: @location_default ref
+);
+
+commentbinding(
+ int id: @comment ref,
+ int element: @element ref
+);
+
+exprconv(
+ int converted: @expr ref,
+ unique int conversion: @expr ref
+);
+
+compgenerated(unique int id: @element ref);
+
+/**
+ * `destructor_call` destructs the `i`'th entity that should be
+ * destructed following `element`. Note that entities should be
+ * destructed in reverse construction order, so for a given `element`
+ * these should be called from highest to lowest `i`.
+ */
+#keyset[element, destructor_call]
+#keyset[element, i]
+synthetic_destructor_call(
+ int element: @element ref,
+ int i: int ref,
+ int destructor_call: @routineexpr ref
+);
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref
+);
+
+namespace_inline(
+ unique int id: @namespace ref
+);
+
+namespacembrs(
+ int parentid: @namespace ref,
+ unique int memberid: @namespacembr ref
+);
+
+@namespacembr = @declaration | @namespace;
+
+exprparents(
+ int expr_id: @expr ref,
+ int child_index: int ref,
+ int parent_id: @exprparent ref
+);
+
+expr_isload(unique int expr_id: @expr ref);
+
+@cast = @c_style_cast
+ | @const_cast
+ | @dynamic_cast
+ | @reinterpret_cast
+ | @static_cast
+ ;
+
+/*
+case @conversion.kind of
+ 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast
+| 1 = @bool_conversion // conversion to 'bool'
+| 2 = @base_class_conversion // a derived-to-base conversion
+| 3 = @derived_class_conversion // a base-to-derived conversion
+| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member
+| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member
+| 6 = @glvalue_adjust // an adjustment of the type of a glvalue
+| 7 = @prvalue_adjust // an adjustment of the type of a prvalue
+;
+*/
+/**
+ * Describes the semantics represented by a cast expression. This is largely
+ * independent of the source syntax of the cast, so it is separate from the
+ * regular expression kind.
+ */
+conversionkinds(
+ unique int expr_id: @cast ref,
+ int kind: int ref
+);
+
+@conversion = @cast
+ | @array_to_pointer
+ | @parexpr
+ | @reference_to
+ | @ref_indirect
+ | @temp_init
+ ;
+
+/*
+case @funbindexpr.kind of
+ 0 = @normal_call // a normal call
+| 1 = @virtual_call // a virtual call
+| 2 = @adl_call // a call whose target is only found by ADL
+;
+*/
+iscall(unique int caller: @funbindexpr ref, int kind: int ref);
+
+numtemplatearguments(
+ unique int expr_id: @expr ref,
+ int num: int ref
+);
+
+specialnamequalifyingelements(
+ unique int id: @specialnamequalifyingelement,
+ unique string name: string ref
+);
+
+@namequalifiableelement = @expr | @namequalifier;
+@namequalifyingelement = @namespace
+ | @specialnamequalifyingelement
+ | @usertype;
+
+namequalifiers(
+ unique int id: @namequalifier,
+ unique int qualifiableelement: @namequalifiableelement ref,
+ int qualifyingelement: @namequalifyingelement ref,
+ int location: @location_default ref
+);
+
+varbind(
+ int expr: @varbindexpr ref,
+ int var: @accessible ref
+);
+
+funbind(
+ int expr: @funbindexpr ref,
+ int fun: @function ref
+);
+
+@any_new_expr = @new_expr
+ | @new_array_expr;
+
+@new_or_delete_expr = @any_new_expr
+ | @delete_expr
+ | @delete_array_expr;
+
+@prefix_crement_expr = @preincrexpr | @predecrexpr;
+
+@postfix_crement_expr = @postincrexpr | @postdecrexpr;
+
+@increment_expr = @preincrexpr | @postincrexpr;
+
+@decrement_expr = @predecrexpr | @postdecrexpr;
+
+@crement_expr = @increment_expr | @decrement_expr;
+
+@un_arith_op_expr = @arithnegexpr
+ | @unaryplusexpr
+ | @conjugation
+ | @realpartexpr
+ | @imagpartexpr
+ | @crement_expr
+ ;
+
+@un_bitwise_op_expr = @complementexpr;
+
+@un_log_op_expr = @notexpr;
+
+@un_op_expr = @address_of
+ | @indirect
+ | @un_arith_op_expr
+ | @un_bitwise_op_expr
+ | @builtinaddressof
+ | @vec_fill
+ | @un_log_op_expr
+ | @co_await
+ | @co_yield
+ ;
+
+@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr;
+
+@cmp_op_expr = @eq_op_expr | @rel_op_expr;
+
+@eq_op_expr = @eqexpr | @neexpr;
+
+@rel_op_expr = @gtexpr
+ | @ltexpr
+ | @geexpr
+ | @leexpr
+ | @spaceshipexpr
+ ;
+
+@bin_bitwise_op_expr = @lshiftexpr
+ | @rshiftexpr
+ | @andexpr
+ | @orexpr
+ | @xorexpr
+ ;
+
+@p_arith_op_expr = @paddexpr
+ | @psubexpr
+ | @pdiffexpr
+ ;
+
+@bin_arith_op_expr = @addexpr
+ | @subexpr
+ | @mulexpr
+ | @divexpr
+ | @remexpr
+ | @jmulexpr
+ | @jdivexpr
+ | @fjaddexpr
+ | @jfaddexpr
+ | @fjsubexpr
+ | @jfsubexpr
+ | @minexpr
+ | @maxexpr
+ | @p_arith_op_expr
+ ;
+
+@bin_op_expr = @bin_arith_op_expr
+ | @bin_bitwise_op_expr
+ | @cmp_op_expr
+ | @bin_log_op_expr
+ ;
+
+@op_expr = @un_op_expr
+ | @bin_op_expr
+ | @assign_expr
+ | @conditionalexpr
+ ;
+
+@assign_arith_expr = @assignaddexpr
+ | @assignsubexpr
+ | @assignmulexpr
+ | @assigndivexpr
+ | @assignremexpr
+ ;
+
+@assign_bitwise_expr = @assignandexpr
+ | @assignorexpr
+ | @assignxorexpr
+ | @assignlshiftexpr
+ | @assignrshiftexpr
+ | @assignpaddexpr
+ | @assignpsubexpr
+ ;
+
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr
+
+@assign_expr = @assignexpr | @assign_op_expr
+
+/*
+ case @allocator.form of
+ 0 = plain
+ | 1 = alignment
+ ;
+*/
+
+/**
+ * The allocator function associated with a `new` or `new[]` expression.
+ * The `form` column specified whether the allocation call contains an alignment
+ * argument.
+ */
+expr_allocator(
+ unique int expr: @any_new_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/*
+ case @deallocator.form of
+ 0 = plain
+ | 1 = size
+ | 2 = alignment
+ | 3 = size_and_alignment
+ ;
+*/
+
+/**
+ * The deallocator function associated with a `delete`, `delete[]`, `new`, or
+ * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the
+ * one used to free memory if the initialization throws an exception.
+ * The `form` column specifies whether the deallocation call contains a size
+ * argument, and alignment argument, or both.
+ */
+expr_deallocator(
+ unique int expr: @new_or_delete_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/**
+ * Holds if the `@conditionalexpr` is of the two operand form
+ * `guard ? : false`.
+ */
+expr_cond_two_operand(
+ unique int cond: @conditionalexpr ref
+);
+
+/**
+ * The guard of `@conditionalexpr` `guard ? true : false`
+ */
+expr_cond_guard(
+ unique int cond: @conditionalexpr ref,
+ int guard: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` holds. For the two operand form
+ * `guard ?: false` consider using `expr_cond_guard` instead.
+ */
+expr_cond_true(
+ unique int cond: @conditionalexpr ref,
+ int true: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` does not hold.
+ */
+expr_cond_false(
+ unique int cond: @conditionalexpr ref,
+ int false: @expr ref
+);
+
+/** A string representation of the value. */
+values(
+ unique int id: @value,
+ string str: string ref
+);
+
+/** The actual text in the source code for the value, if any. */
+valuetext(
+ unique int id: @value ref,
+ string text: string ref
+);
+
+valuebind(
+ int val: @value ref,
+ unique int expr: @expr ref
+);
+
+fieldoffsets(
+ unique int id: @variable ref,
+ int byteoffset: int ref,
+ int bitoffset: int ref
+);
+
+bitfield(
+ unique int id: @variable ref,
+ int bits: int ref,
+ int declared_bits: int ref
+);
+
+/* TODO
+memberprefix(
+ int member: @expr ref,
+ int prefix: @expr ref
+);
+*/
+
+/*
+ kind(1) = mbrcallexpr
+ kind(2) = mbrptrcallexpr
+ kind(3) = mbrptrmbrcallexpr
+ kind(4) = ptrmbrptrmbrcallexpr
+ kind(5) = mbrreadexpr // x.y
+ kind(6) = mbrptrreadexpr // p->y
+ kind(7) = mbrptrmbrreadexpr // x.*pm
+ kind(8) = mbrptrmbrptrreadexpr // x->*pm
+ kind(9) = staticmbrreadexpr // static x.y
+ kind(10) = staticmbrptrreadexpr // static p->y
+*/
+/* TODO
+memberaccess(
+ int member: @expr ref,
+ int kind: int ref
+);
+*/
+
+initialisers(
+ unique int init: @initialiser,
+ int var: @accessible ref,
+ unique int expr: @expr ref,
+ int location: @location_expr ref
+);
+
+braced_initialisers(
+ int init: @initialiser ref
+);
+
+/**
+ * An ancestor for the expression, for cases in which we cannot
+ * otherwise find the expression's parent.
+ */
+expr_ancestor(
+ int exp: @expr ref,
+ int ancestor: @element ref
+);
+
+exprs(
+ unique int id: @expr,
+ int kind: int ref,
+ int location: @location_expr ref
+);
+
+/*
+ case @value.category of
+ 1 = prval
+ | 2 = xval
+ | 3 = lval
+ ;
+*/
+expr_types(
+ int id: @expr ref,
+ int typeid: @type ref,
+ int value_category: int ref
+);
+
+case @expr.kind of
+ 1 = @errorexpr
+| 2 = @address_of // & AddressOfExpr
+| 3 = @reference_to // ReferenceToExpr (implicit?)
+| 4 = @indirect // * PointerDereferenceExpr
+| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?)
+// ...
+| 8 = @array_to_pointer // (???)
+| 9 = @vacuous_destructor_call // VacuousDestructorCall
+// ...
+| 11 = @assume // Microsoft
+| 12 = @parexpr
+| 13 = @arithnegexpr
+| 14 = @unaryplusexpr
+| 15 = @complementexpr
+| 16 = @notexpr
+| 17 = @conjugation // GNU ~ operator
+| 18 = @realpartexpr // GNU __real
+| 19 = @imagpartexpr // GNU __imag
+| 20 = @postincrexpr
+| 21 = @postdecrexpr
+| 22 = @preincrexpr
+| 23 = @predecrexpr
+| 24 = @conditionalexpr
+| 25 = @addexpr
+| 26 = @subexpr
+| 27 = @mulexpr
+| 28 = @divexpr
+| 29 = @remexpr
+| 30 = @jmulexpr // C99 mul imaginary
+| 31 = @jdivexpr // C99 div imaginary
+| 32 = @fjaddexpr // C99 add real + imaginary
+| 33 = @jfaddexpr // C99 add imaginary + real
+| 34 = @fjsubexpr // C99 sub real - imaginary
+| 35 = @jfsubexpr // C99 sub imaginary - real
+| 36 = @paddexpr // pointer add (pointer + int or int + pointer)
+| 37 = @psubexpr // pointer sub (pointer - integer)
+| 38 = @pdiffexpr // difference between two pointers
+| 39 = @lshiftexpr
+| 40 = @rshiftexpr
+| 41 = @andexpr
+| 42 = @orexpr
+| 43 = @xorexpr
+| 44 = @eqexpr
+| 45 = @neexpr
+| 46 = @gtexpr
+| 47 = @ltexpr
+| 48 = @geexpr
+| 49 = @leexpr
+| 50 = @minexpr // GNU minimum
+| 51 = @maxexpr // GNU maximum
+| 52 = @assignexpr
+| 53 = @assignaddexpr
+| 54 = @assignsubexpr
+| 55 = @assignmulexpr
+| 56 = @assigndivexpr
+| 57 = @assignremexpr
+| 58 = @assignlshiftexpr
+| 59 = @assignrshiftexpr
+| 60 = @assignandexpr
+| 61 = @assignorexpr
+| 62 = @assignxorexpr
+| 63 = @assignpaddexpr // assign pointer add
+| 64 = @assignpsubexpr // assign pointer sub
+| 65 = @andlogicalexpr
+| 66 = @orlogicalexpr
+| 67 = @commaexpr
+| 68 = @subscriptexpr // access to member of an array, e.g., a[5]
+// ... 69 @objc_subscriptexpr deprecated
+// ... 70 @cmdaccess deprecated
+// ...
+| 73 = @virtfunptrexpr
+| 74 = @callexpr
+// ... 75 @msgexpr_normal deprecated
+// ... 76 @msgexpr_super deprecated
+// ... 77 @atselectorexpr deprecated
+// ... 78 @atprotocolexpr deprecated
+| 79 = @vastartexpr
+| 80 = @vaargexpr
+| 81 = @vaendexpr
+| 82 = @vacopyexpr
+// ... 83 @atencodeexpr deprecated
+| 84 = @varaccess
+| 85 = @thisaccess
+// ... 86 @objc_box_expr deprecated
+| 87 = @new_expr
+| 88 = @delete_expr
+| 89 = @throw_expr
+| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2)
+| 91 = @braced_init_list
+| 92 = @type_id
+| 93 = @runtime_sizeof
+| 94 = @runtime_alignof
+| 95 = @sizeof_pack
+| 96 = @expr_stmt // GNU extension
+| 97 = @routineexpr
+| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....)
+| 99 = @offsetofexpr // offsetof ::= type and field
+| 100 = @hasassignexpr // __has_assign ::= type
+| 101 = @hascopyexpr // __has_copy ::= type
+| 102 = @hasnothrowassign // __has_nothrow_assign ::= type
+| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type
+| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type
+| 105 = @hastrivialassign // __has_trivial_assign ::= type
+| 106 = @hastrivialconstr // __has_trivial_constructor ::= type
+| 107 = @hastrivialcopy // __has_trivial_copy ::= type
+| 108 = @hasuserdestr // __has_user_destructor ::= type
+| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type
+| 110 = @isabstractexpr // __is_abstract ::= type
+| 111 = @isbaseofexpr // __is_base_of ::= type type
+| 112 = @isclassexpr // __is_class ::= type
+| 113 = @isconvtoexpr // __is_convertible_to ::= type type
+| 114 = @isemptyexpr // __is_empty ::= type
+| 115 = @isenumexpr // __is_enum ::= type
+| 116 = @ispodexpr // __is_pod ::= type
+| 117 = @ispolyexpr // __is_polymorphic ::= type
+| 118 = @isunionexpr // __is_union ::= type
+| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type
+| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof
+// ...
+| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type
+| 123 = @literal
+| 124 = @uuidof
+| 127 = @aggregateliteral
+| 128 = @delete_array_expr
+| 129 = @new_array_expr
+// ... 130 @objc_array_literal deprecated
+// ... 131 @objc_dictionary_literal deprecated
+| 132 = @foldexpr
+// ...
+| 200 = @ctordirectinit
+| 201 = @ctorvirtualinit
+| 202 = @ctorfieldinit
+| 203 = @ctordelegatinginit
+| 204 = @dtordirectdestruct
+| 205 = @dtorvirtualdestruct
+| 206 = @dtorfielddestruct
+// ...
+| 210 = @static_cast
+| 211 = @reinterpret_cast
+| 212 = @const_cast
+| 213 = @dynamic_cast
+| 214 = @c_style_cast
+| 215 = @lambdaexpr
+| 216 = @param_ref
+| 217 = @noopexpr
+// ...
+| 294 = @istriviallyconstructibleexpr
+| 295 = @isdestructibleexpr
+| 296 = @isnothrowdestructibleexpr
+| 297 = @istriviallydestructibleexpr
+| 298 = @istriviallyassignableexpr
+| 299 = @isnothrowassignableexpr
+| 300 = @istrivialexpr
+| 301 = @isstandardlayoutexpr
+| 302 = @istriviallycopyableexpr
+| 303 = @isliteraltypeexpr
+| 304 = @hastrivialmoveconstructorexpr
+| 305 = @hastrivialmoveassignexpr
+| 306 = @hasnothrowmoveassignexpr
+| 307 = @isconstructibleexpr
+| 308 = @isnothrowconstructibleexpr
+| 309 = @hasfinalizerexpr
+| 310 = @isdelegateexpr
+| 311 = @isinterfaceclassexpr
+| 312 = @isrefarrayexpr
+| 313 = @isrefclassexpr
+| 314 = @issealedexpr
+| 315 = @issimplevalueclassexpr
+| 316 = @isvalueclassexpr
+| 317 = @isfinalexpr
+| 319 = @noexceptexpr
+| 320 = @builtinshufflevector
+| 321 = @builtinchooseexpr
+| 322 = @builtinaddressof
+| 323 = @vec_fill
+| 324 = @builtinconvertvector
+| 325 = @builtincomplex
+| 326 = @spaceshipexpr
+| 327 = @co_await
+| 328 = @co_yield
+| 329 = @temp_init
+| 330 = @isassignable
+| 331 = @isaggregate
+| 332 = @hasuniqueobjectrepresentations
+| 333 = @builtinbitcast
+| 334 = @builtinshuffle
+;
+
+@var_args_expr = @vastartexpr
+ | @vaendexpr
+ | @vaargexpr
+ | @vacopyexpr
+ ;
+
+@builtin_op = @var_args_expr
+ | @noopexpr
+ | @offsetofexpr
+ | @intaddrexpr
+ | @hasassignexpr
+ | @hascopyexpr
+ | @hasnothrowassign
+ | @hasnothrowconstr
+ | @hasnothrowcopy
+ | @hastrivialassign
+ | @hastrivialconstr
+ | @hastrivialcopy
+ | @hastrivialdestructor
+ | @hasuserdestr
+ | @hasvirtualdestr
+ | @isabstractexpr
+ | @isbaseofexpr
+ | @isclassexpr
+ | @isconvtoexpr
+ | @isemptyexpr
+ | @isenumexpr
+ | @ispodexpr
+ | @ispolyexpr
+ | @isunionexpr
+ | @typescompexpr
+ | @builtinshufflevector
+ | @builtinconvertvector
+ | @builtinaddressof
+ | @istriviallyconstructibleexpr
+ | @isdestructibleexpr
+ | @isnothrowdestructibleexpr
+ | @istriviallydestructibleexpr
+ | @istriviallyassignableexpr
+ | @isnothrowassignableexpr
+ | @isstandardlayoutexpr
+ | @istriviallycopyableexpr
+ | @isliteraltypeexpr
+ | @hastrivialmoveconstructorexpr
+ | @hastrivialmoveassignexpr
+ | @hasnothrowmoveassignexpr
+ | @isconstructibleexpr
+ | @isnothrowconstructibleexpr
+ | @hasfinalizerexpr
+ | @isdelegateexpr
+ | @isinterfaceclassexpr
+ | @isrefarrayexpr
+ | @isrefclassexpr
+ | @issealedexpr
+ | @issimplevalueclassexpr
+ | @isvalueclassexpr
+ | @isfinalexpr
+ | @builtinchooseexpr
+ | @builtincomplex
+ | @isassignable
+ | @isaggregate
+ | @hasuniqueobjectrepresentations
+ | @builtinbitcast
+ | @builtinshuffle
+ ;
+
+new_allocated_type(
+ unique int expr: @new_expr ref,
+ int type_id: @type ref
+);
+
+new_array_allocated_type(
+ unique int expr: @new_array_expr ref,
+ int type_id: @type ref
+);
+
+/**
+ * The field being initialized by an initializer expression within an aggregate
+ * initializer for a class/struct/union.
+ */
+#keyset[aggregate, field]
+aggregate_field_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int field: @membervariable ref
+);
+
+/**
+ * The index of the element being initialized by an initializer expression
+ * within an aggregate initializer for an array.
+ */
+#keyset[aggregate, element_index]
+aggregate_array_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int element_index: int ref
+);
+
+@ctorinit = @ctordirectinit
+ | @ctorvirtualinit
+ | @ctorfieldinit
+ | @ctordelegatinginit;
+@dtordestruct = @dtordirectdestruct
+ | @dtorvirtualdestruct
+ | @dtorfielddestruct;
+
+
+condition_decl_bind(
+ unique int expr: @condition_decl ref,
+ unique int decl: @declaration ref
+);
+
+typeid_bind(
+ unique int expr: @type_id ref,
+ int type_id: @type ref
+);
+
+uuidof_bind(
+ unique int expr: @uuidof ref,
+ int type_id: @type ref
+);
+
+@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof;
+
+sizeof_bind(
+ unique int expr: @runtime_sizeof_or_alignof ref,
+ int type_id: @type ref
+);
+
+code_block(
+ unique int block: @literal ref,
+ unique int routine: @function ref
+);
+
+lambdas(
+ unique int expr: @lambdaexpr ref,
+ string default_capture: string ref,
+ boolean has_explicit_return_type: boolean ref
+);
+
+lambda_capture(
+ unique int id: @lambdacapture,
+ int lambda: @lambdaexpr ref,
+ int index: int ref,
+ int field: @membervariable ref,
+ boolean captured_by_reference: boolean ref,
+ boolean is_implicit: boolean ref,
+ int location: @location_default ref
+);
+
+@funbindexpr = @routineexpr
+ | @new_expr
+ | @delete_expr
+ | @delete_array_expr
+ | @ctordirectinit
+ | @ctorvirtualinit
+ | @ctordelegatinginit
+ | @dtordirectdestruct
+ | @dtorvirtualdestruct;
+
+@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct;
+@addressable = @function | @variable ;
+@accessible = @addressable | @enumconstant ;
+
+@access = @varaccess | @routineexpr ;
+
+fold(
+ int expr: @foldexpr ref,
+ string operator: string ref,
+ boolean is_left_fold: boolean ref
+);
+
+stmts(
+ unique int id: @stmt,
+ int kind: int ref,
+ int location: @location_stmt ref
+);
+
+case @stmt.kind of
+ 1 = @stmt_expr
+| 2 = @stmt_if
+| 3 = @stmt_while
+| 4 = @stmt_goto
+| 5 = @stmt_label
+| 6 = @stmt_return
+| 7 = @stmt_block
+| 8 = @stmt_end_test_while // do { ... } while ( ... )
+| 9 = @stmt_for
+| 10 = @stmt_switch_case
+| 11 = @stmt_switch
+| 13 = @stmt_asm // "asm" statement or the body of an asm function
+| 15 = @stmt_try_block
+| 16 = @stmt_microsoft_try // Microsoft
+| 17 = @stmt_decl
+| 18 = @stmt_set_vla_size // C99
+| 19 = @stmt_vla_decl // C99
+| 25 = @stmt_assigned_goto // GNU
+| 26 = @stmt_empty
+| 27 = @stmt_continue
+| 28 = @stmt_break
+| 29 = @stmt_range_based_for // C++11
+// ... 30 @stmt_at_autoreleasepool_block deprecated
+// ... 31 @stmt_objc_for_in deprecated
+// ... 32 @stmt_at_synchronized deprecated
+| 33 = @stmt_handler
+// ... 34 @stmt_finally_end deprecated
+| 35 = @stmt_constexpr_if
+| 37 = @stmt_co_return
+;
+
+type_vla(
+ int type_id: @type ref,
+ int decl: @stmt_vla_decl ref
+);
+
+variable_vla(
+ int var: @variable ref,
+ int decl: @stmt_vla_decl ref
+);
+
+if_initialization(
+ unique int if_stmt: @stmt_if ref,
+ int init_id: @stmt ref
+);
+
+if_then(
+ unique int if_stmt: @stmt_if ref,
+ int then_id: @stmt ref
+);
+
+if_else(
+ unique int if_stmt: @stmt_if ref,
+ int else_id: @stmt ref
+);
+
+constexpr_if_initialization(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int init_id: @stmt ref
+);
+
+constexpr_if_then(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int then_id: @stmt ref
+);
+
+constexpr_if_else(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int else_id: @stmt ref
+);
+
+while_body(
+ unique int while_stmt: @stmt_while ref,
+ int body_id: @stmt ref
+);
+
+do_body(
+ unique int do_stmt: @stmt_end_test_while ref,
+ int body_id: @stmt ref
+);
+
+switch_initialization(
+ unique int switch_stmt: @stmt_switch ref,
+ int init_id: @stmt ref
+);
+
+#keyset[switch_stmt, index]
+switch_case(
+ int switch_stmt: @stmt_switch ref,
+ int index: int ref,
+ int case_id: @stmt_switch_case ref
+);
+
+switch_body(
+ unique int switch_stmt: @stmt_switch ref,
+ int body_id: @stmt ref
+);
+
+for_initialization(
+ unique int for_stmt: @stmt_for ref,
+ int init_id: @stmt ref
+);
+
+for_condition(
+ unique int for_stmt: @stmt_for ref,
+ int condition_id: @expr ref
+);
+
+for_update(
+ unique int for_stmt: @stmt_for ref,
+ int update_id: @expr ref
+);
+
+for_body(
+ unique int for_stmt: @stmt_for ref,
+ int body_id: @stmt ref
+);
+
+@stmtparent = @stmt | @expr_stmt ;
+stmtparents(
+ unique int id: @stmt ref,
+ int index: int ref,
+ int parent: @stmtparent ref
+);
+
+ishandler(unique int block: @stmt_block ref);
+
+@cfgnode = @stmt | @expr | @function | @initialiser ;
+
+stmt_decl_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl: @declaration ref
+);
+
+stmt_decl_entry_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl_entry: @element ref
+);
+
+@functionorblock = @function | @stmt_block;
+
+blockscope(
+ unique int block: @stmt_block ref,
+ int enclosing: @functionorblock ref
+);
+
+@jump = @stmt_goto | @stmt_break | @stmt_continue;
+
+@jumporlabel = @jump | @stmt_label | @literal;
+
+jumpinfo(
+ unique int id: @jumporlabel ref,
+ string str: string ref,
+ int target: @stmt ref
+);
+
+preprocdirects(
+ unique int id: @preprocdirect,
+ int kind: int ref,
+ int location: @location_default ref
+);
+case @preprocdirect.kind of
+ 0 = @ppd_if
+| 1 = @ppd_ifdef
+| 2 = @ppd_ifndef
+| 3 = @ppd_elif
+| 4 = @ppd_else
+| 5 = @ppd_endif
+| 6 = @ppd_plain_include
+| 7 = @ppd_define
+| 8 = @ppd_undef
+| 9 = @ppd_line
+| 10 = @ppd_error
+| 11 = @ppd_pragma
+| 12 = @ppd_objc_import
+| 13 = @ppd_include_next
+| 18 = @ppd_warning
+;
+
+@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next;
+
+@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif;
+
+preprocpair(
+ int begin : @ppd_branch ref,
+ int elseelifend : @preprocdirect ref
+);
+
+preproctrue(int branch : @ppd_branch ref);
+preprocfalse(int branch : @ppd_branch ref);
+
+preproctext(
+ unique int id: @preprocdirect ref,
+ string head: string ref,
+ string body: string ref
+);
+
+includes(
+ unique int id: @ppd_include ref,
+ int included: @file ref
+);
+
+link_targets(
+ unique int id: @link_target,
+ int binary: @file ref
+);
+
+link_parent(
+ int element : @element ref,
+ int link_target : @link_target ref
+);
+
+/* XML Files */
+
+xmlEncoding(unique int id: @file ref, string encoding: string ref);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ string root: string ref,
+ string publicId: string ref,
+ string systemId: string ref,
+ int fileid: @file ref
+);
+
+xmlElements(
+ unique int id: @xmlelement,
+ string name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ string name: string ref,
+ string value: string ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlNs(
+ int id: @xmlnamespace,
+ string prefixName: string ref,
+ string URI: string ref,
+ int fileid: @file ref
+);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref
+);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref
+);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref
+);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref
+);
+
+@xmllocatable = @xmlcharacters
+ | @xmlelement
+ | @xmlcomment
+ | @xmlattribute
+ | @xmldtd
+ | @file
+ | @xmlnamespace;
diff --git a/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/upgrade.properties b/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/upgrade.properties
new file mode 100644
index 00000000000..b8154844dbd
--- /dev/null
+++ b/cpp/downgrades/34549c3b0937002f11037d01822ebe99442c1402/upgrade.properties
@@ -0,0 +1,4 @@
+description: Support all constant attribute arguments
+compatibility: backwards
+attribute_arg_constant.rel: delete
+attribute_args.rel: run attribute_args.qlo
diff --git a/cpp/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md
index 52dd2c7a843..9b4761ec2ce 100644
--- a/cpp/ql/lib/CHANGELOG.md
+++ b/cpp/ql/lib/CHANGELOG.md
@@ -1,3 +1,25 @@
+## 0.3.2
+
+### Bug Fixes
+
+* Under certain circumstances a variable declaration that is not also a definition could be associated with a `Variable` that did not have the definition as a `VariableDeclarationEntry`. This is now fixed, and a unique `Variable` will exist that has both the declaration and the definition as a `VariableDeclarationEntry`.
+
+## 0.3.1
+
+### Minor Analysis Improvements
+
+* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the C++ logical "and", and variable declarations in conditions.
+
+## 0.3.0
+
+### Deprecated APIs
+
+* The `BarrierGuard` class has been deprecated. Such barriers and sanitizers can now instead be created using the new `BarrierGuard` parameterized module.
+
+### Bug Fixes
+
+* `UserType.getADeclarationEntry()` now yields all forward declarations when the user type is a `class`, `struct`, or `union`.
+
## 0.2.3
### New Features
diff --git a/cpp/ql/src/IDEContextual.qll b/cpp/ql/lib/IDEContextual.qll
similarity index 100%
rename from cpp/ql/src/IDEContextual.qll
rename to cpp/ql/lib/IDEContextual.qll
diff --git a/cpp/ql/lib/change-notes/2022-05-30-braced-initializers.md b/cpp/ql/lib/change-notes/2022-05-30-braced-initializers.md
deleted file mode 100644
index 8a31f06ab98..00000000000
--- a/cpp/ql/lib/change-notes/2022-05-30-braced-initializers.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: feature
----
-* An `isBraced` predicate was added to the `Initializer` class which holds when a C++ braced initializer was used in the initialization.
diff --git a/cpp/ql/lib/change-notes/2022-06-22-class-declaration-entry-fix.md b/cpp/ql/lib/change-notes/2022-06-22-class-declaration-entry-fix.md
deleted file mode 100644
index fb301705e79..00000000000
--- a/cpp/ql/lib/change-notes/2022-06-22-class-declaration-entry-fix.md
+++ /dev/null
@@ -1,4 +0,0 @@
----
-category: fix
----
-* `UserType.getADeclarationEntry()` now yields all forward declarations when the user type is a `class`, `struct`, or `union`.
diff --git a/cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md b/cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md
new file mode 100644
index 00000000000..ce931ef8de0
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-06-23-global-var-flow.md
@@ -0,0 +1,4 @@
+---
+category: majorAnalysis
+---
+* The IR dataflow library now includes flow through global variables. This enables new findings in many scenarios.
diff --git a/cpp/ql/lib/change-notes/2022-07-26-additional-builtin-support.md b/cpp/ql/lib/change-notes/2022-07-26-additional-builtin-support.md
new file mode 100644
index 00000000000..2e4d7db69a5
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-07-26-additional-builtin-support.md
@@ -0,0 +1,4 @@
+---
+category: feature
+---
+* Added subclasses of `BuiltInOperations` for `__builtin_bit_cast`, `__builtin_shuffle`, `__has_unique_object_representations`, `__is_aggregate`, and `__is_assignable`.
diff --git a/cpp/ql/lib/change-notes/2022-08-02-must-flow-local-only-flow.md b/cpp/ql/lib/change-notes/2022-08-02-must-flow-local-only-flow.md
new file mode 100644
index 00000000000..820822a5396
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-08-02-must-flow-local-only-flow.md
@@ -0,0 +1,4 @@
+---
+category: feature
+---
+* A new class predicate `MustFlowConfiguration::allowInterproceduralFlow` has been added to the `semmle.code.cpp.ir.dataflow.MustFlow` library. The new predicate can be overridden to disable interprocedural flow.
diff --git a/cpp/ql/lib/change-notes/2022-08-10-constant-attribute-argument-support.md b/cpp/ql/lib/change-notes/2022-08-10-constant-attribute-argument-support.md
new file mode 100644
index 00000000000..056190026a8
--- /dev/null
+++ b/cpp/ql/lib/change-notes/2022-08-10-constant-attribute-argument-support.md
@@ -0,0 +1,4 @@
+---
+category: feature
+---
+* Added a predicate `getValueConstant` to `AttributeArgument` that yields the argument value as an `Expr` when the value is a constant expression.
diff --git a/cpp/ql/lib/change-notes/released/0.3.0.md b/cpp/ql/lib/change-notes/released/0.3.0.md
new file mode 100644
index 00000000000..8c45dc21817
--- /dev/null
+++ b/cpp/ql/lib/change-notes/released/0.3.0.md
@@ -0,0 +1,9 @@
+## 0.3.0
+
+### Deprecated APIs
+
+* The `BarrierGuard` class has been deprecated. Such barriers and sanitizers can now instead be created using the new `BarrierGuard` parameterized module.
+
+### Bug Fixes
+
+* `UserType.getADeclarationEntry()` now yields all forward declarations when the user type is a `class`, `struct`, or `union`.
diff --git a/cpp/ql/lib/change-notes/released/0.3.1.md b/cpp/ql/lib/change-notes/released/0.3.1.md
new file mode 100644
index 00000000000..d5b251c42ab
--- /dev/null
+++ b/cpp/ql/lib/change-notes/released/0.3.1.md
@@ -0,0 +1,5 @@
+## 0.3.1
+
+### Minor Analysis Improvements
+
+* `AnalysedExpr::isNullCheck` and `AnalysedExpr::isValidCheck` have been updated to handle variable accesses on the left-hand side of the C++ logical "and", and variable declarations in conditions.
diff --git a/cpp/ql/lib/change-notes/released/0.3.2.md b/cpp/ql/lib/change-notes/released/0.3.2.md
new file mode 100644
index 00000000000..9d3ca0cca67
--- /dev/null
+++ b/cpp/ql/lib/change-notes/released/0.3.2.md
@@ -0,0 +1,5 @@
+## 0.3.2
+
+### Bug Fixes
+
+* Under certain circumstances a variable declaration that is not also a definition could be associated with a `Variable` that did not have the definition as a `VariableDeclarationEntry`. This is now fixed, and a unique `Variable` will exist that has both the declaration and the definition as a `VariableDeclarationEntry`.
diff --git a/cpp/ql/lib/codeql-pack.release.yml b/cpp/ql/lib/codeql-pack.release.yml
index 0b605901b42..18c64250f42 100644
--- a/cpp/ql/lib/codeql-pack.release.yml
+++ b/cpp/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.2.3
+lastReleaseVersion: 0.3.2
diff --git a/cpp/ql/src/definitions.qll b/cpp/ql/lib/definitions.qll
similarity index 100%
rename from cpp/ql/src/definitions.qll
rename to cpp/ql/lib/definitions.qll
diff --git a/cpp/ql/src/localDefinitions.ql b/cpp/ql/lib/localDefinitions.ql
similarity index 100%
rename from cpp/ql/src/localDefinitions.ql
rename to cpp/ql/lib/localDefinitions.ql
diff --git a/cpp/ql/src/localReferences.ql b/cpp/ql/lib/localReferences.ql
similarity index 100%
rename from cpp/ql/src/localReferences.ql
rename to cpp/ql/lib/localReferences.ql
diff --git a/cpp/ql/src/printAst.ql b/cpp/ql/lib/printAst.ql
similarity index 100%
rename from cpp/ql/src/printAst.ql
rename to cpp/ql/lib/printAst.ql
diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml
index 28cddcb3b00..06e68dba48c 100644
--- a/cpp/ql/lib/qlpack.yml
+++ b/cpp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-all
-version: 0.3.0-dev
+version: 0.3.3-dev
groups: cpp
dbscheme: semmlecode.cpp.dbscheme
extractor: cpp
diff --git a/cpp/ql/lib/semmle/code/cpp/Element.qll b/cpp/ql/lib/semmle/code/cpp/Element.qll
index 9e4ef7f5f8d..79df774d80f 100644
--- a/cpp/ql/lib/semmle/code/cpp/Element.qll
+++ b/cpp/ql/lib/semmle/code/cpp/Element.qll
@@ -6,6 +6,7 @@
import semmle.code.cpp.Location
private import semmle.code.cpp.Enclosing
private import semmle.code.cpp.internal.ResolveClass
+private import semmle.code.cpp.internal.ResolveGlobalVariable
/**
* Get the `Element` that represents this `@element`.
@@ -28,9 +29,12 @@ Element mkElement(@element e) { unresolveElement(result) = e }
pragma[inline]
@element unresolveElement(Element e) {
not result instanceof @usertype and
+ not result instanceof @variable and
result = e
or
e = resolveClass(result)
+ or
+ e = resolveGlobalVariable(result)
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/Specifier.qll b/cpp/ql/lib/semmle/code/cpp/Specifier.qll
index 7a13b13e8c2..19622bbdc56 100644
--- a/cpp/ql/lib/semmle/code/cpp/Specifier.qll
+++ b/cpp/ql/lib/semmle/code/cpp/Specifier.qll
@@ -12,7 +12,7 @@ private import semmle.code.cpp.internal.ResolveClass
class Specifier extends Element, @specifier {
/** Gets a dummy location for the specifier. */
override Location getLocation() {
- suppressUnusedThis(this) and
+ exists(this) and
result instanceof UnknownDefaultLocation
}
@@ -256,9 +256,13 @@ class AttributeArgument extends Element, @attribute_arg {
/**
* Gets the text for the value of this argument, if its value is
- * a string or a number.
+ * a constant or a token.
*/
- string getValueText() { attribute_arg_value(underlyingElement(this), result) }
+ string getValueText() {
+ if underlyingElement(this) instanceof @attribute_arg_constant_expr
+ then result = this.getValueConstant().getValue()
+ else attribute_arg_value(underlyingElement(this), result)
+ }
/**
* Gets the value of this argument, if its value is integral.
@@ -270,6 +274,13 @@ class AttributeArgument extends Element, @attribute_arg {
*/
Type getValueType() { attribute_arg_type(underlyingElement(this), unresolveElement(result)) }
+ /**
+ * Gets the value of this argument, if its value is a constant.
+ */
+ Expr getValueConstant() {
+ attribute_arg_constant(underlyingElement(this), unresolveElement(result))
+ }
+
/**
* Gets the attribute to which this is an argument.
*/
@@ -294,11 +305,12 @@ class AttributeArgument extends Element, @attribute_arg {
(
if underlyingElement(this) instanceof @attribute_arg_type
then tail = this.getValueType().getName()
- else tail = this.getValueText()
+ else
+ if underlyingElement(this) instanceof @attribute_arg_constant_expr
+ then tail = this.getValueConstant().toString()
+ else tail = this.getValueText()
) and
result = prefix + tail
)
}
}
-
-private predicate suppressUnusedThis(Specifier s) { any() }
diff --git a/cpp/ql/lib/semmle/code/cpp/Variable.qll b/cpp/ql/lib/semmle/code/cpp/Variable.qll
index 2e3d6bf3ca2..7e188980b9c 100644
--- a/cpp/ql/lib/semmle/code/cpp/Variable.qll
+++ b/cpp/ql/lib/semmle/code/cpp/Variable.qll
@@ -6,6 +6,7 @@ import semmle.code.cpp.Element
import semmle.code.cpp.exprs.Access
import semmle.code.cpp.Initializer
private import semmle.code.cpp.internal.ResolveClass
+private import semmle.code.cpp.internal.ResolveGlobalVariable
/**
* A C/C++ variable. For example, in the following code there are four
@@ -32,6 +33,8 @@ private import semmle.code.cpp.internal.ResolveClass
* can have multiple declarations.
*/
class Variable extends Declaration, @variable {
+ Variable() { isVariable(underlyingElement(this)) }
+
override string getAPrimaryQlClass() { result = "Variable" }
/** Gets the initializer of this variable, if any. */
diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll
index 11a7e8f9e64..ebea83e47e5 100644
--- a/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll
+++ b/cpp/ql/lib/semmle/code/cpp/controlflow/BasicBlocks.qll
@@ -231,7 +231,7 @@ class BasicBlock extends ControlFlowNodeBase {
exists(Function f | f.getBlock() = this)
or
exists(TryStmt t, BasicBlock tryblock |
- // a `Handler` preceeds the `CatchBlock`, and is always the beginning
+ // a `Handler` precedes the `CatchBlock`, and is always the beginning
// of a new `BasicBlock` (see `primitive_basic_block_entry_node`).
this.(Handler).getTryStmt() = t and
tryblock.isReachable() and
diff --git a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll
index 40c975873b4..0fb46f75c94 100644
--- a/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll
+++ b/cpp/ql/lib/semmle/code/cpp/controlflow/Nullness.qll
@@ -46,7 +46,7 @@ predicate nullCheckExpr(Expr checkExpr, Variable var) {
or
exists(LogicalAndExpr op, AnalysedExpr child |
expr = op and
- op.getRightOperand() = child and
+ op.getAnOperand() = child and
nullCheckExpr(child, v)
)
or
@@ -99,7 +99,7 @@ predicate validCheckExpr(Expr checkExpr, Variable var) {
or
exists(LogicalAndExpr op, AnalysedExpr child |
expr = op and
- op.getRightOperand() = child and
+ op.getAnOperand() = child and
validCheckExpr(child, v)
)
or
@@ -169,7 +169,10 @@ class AnalysedExpr extends Expr {
*/
predicate isDef(LocalScopeVariable v) {
this.inCondition() and
- this.(Assignment).getLValue() = v.getAnAccess()
+ (
+ this.(Assignment).getLValue() = v.getAnAccess() or
+ this.(ConditionDeclExpr).getVariableAccess() = v.getAnAccess()
+ )
}
/**
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 7b9e78d2c4b..340bfe280b7 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
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 7b9e78d2c4b..340bfe280b7 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
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 7b9e78d2c4b..340bfe280b7 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
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 7b9e78d2c4b..340bfe280b7 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
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 7b9e78d2c4b..340bfe280b7 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
index da8e7564b3e..79d9f85464e 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -699,7 +699,7 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) {
call.getTarget() = f and
// AST dataflow treats a reference as if it were the referred-to object, while the dataflow
// models treat references as pointers. If the return type of the call is a reference, then
- // look for data flow the the referred-to object, rather than the reference itself.
+ // look for data flow to the referred-to object, rather than the reference itself.
if call.getType().getUnspecifiedType() instanceof ReferenceType
then outModel.isReturnValueDeref()
else outModel.isReturnValue()
diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll
index 309d98cd694..979c9c03940 100644
--- a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll
+++ b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll
@@ -1,5 +1,5 @@
/**
- * Provides classes for modeling built-in operations. Built-in operations are
+ * Provides classes for modeling built-in operations. Built-in operations are
* typically compiler specific and are used by libraries and generated code.
*/
@@ -120,8 +120,8 @@ class BuiltInNoOp extends BuiltInOperation, @noopexpr {
/**
* A C/C++ `__builtin_offsetof` built-in operation (used by some implementations
- * of `offsetof`). The operation retains its semantics even in the presence
- * of an overloaded `operator &`). This is a GNU/Clang extension.
+ * of `offsetof`). The operation retains its semantics even in the presence
+ * of an overloaded `operator &`). This is a gcc/clang extension.
* ```
* struct S {
* int a, b;
@@ -137,8 +137,8 @@ class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr {
/**
* A C/C++ `__INTADDR__` built-in operation (used by some implementations
- * of `offsetof`). The operation retains its semantics even in the presence
- * of an overloaded `operator &`). This is an EDG extension.
+ * of `offsetof`). The operation retains its semantics even in the presence
+ * of an overloaded `operator &`). This is an EDG extension.
* ```
* struct S {
* int a, b;
@@ -173,7 +173,7 @@ class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr {
*
* Returns `true` if the type has a copy constructor.
* ```
- * std::integral_constant< bool, __has_copy(_Tp)> hc;
+ * std::integral_constant hc;
* ```
*/
class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr {
@@ -189,7 +189,7 @@ class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr {
* Returns `true` if a copy assignment operator has an empty exception
* specification.
* ```
- * std::integral_constant< bool, __has_nothrow_assign(_Tp)> hnta;
+ * std::integral_constant hnta;
* ```
*/
class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassign {
@@ -220,7 +220,7 @@ class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothro
*
* Returns `true` if the copy constructor has an empty exception specification.
* ```
- * std::integral_constant< bool, __has_nothrow_copy(MyType) >;
+ * std::integral_constant;
* ```
*/
class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy {
@@ -266,7 +266,7 @@ class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivia
*
* Returns true if the type has a trivial copy constructor.
* ```
- * std::integral_constant< bool, __has_trivial_copy(MyType) > htc;
+ * std::integral_constant htc;
* ```
*/
class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy {
@@ -468,7 +468,7 @@ class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr {
* ```
* template
* struct types_compatible
- * : public integral_constant
+ * : public integral_constant
* { };
* ```
*/
@@ -479,8 +479,7 @@ class BuiltInOperationBuiltInTypesCompatibleP extends BuiltInOperation, @typesco
/**
* A clang `__builtin_shufflevector` expression.
*
- * It outputs a permutation of elements from one or two input vectors.
- * Please see
+ * It outputs a permutation of elements from one or two input vectors. See
* https://releases.llvm.org/3.7.0/tools/clang/docs/LanguageExtensions.html#langext-builtin-shufflevector
* for more information.
* ```
@@ -494,11 +493,29 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu
override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInShuffleVector" }
}
+/**
+ * A gcc `__builtin_shuffle` expression.
+ *
+ * It outputs a permutation of elements from one or two input vectors.
+ * See https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html
+ * for more information.
+ * ```
+ * // Concatenate every other element of 4-element vectors V1 and V2.
+ * M = {0, 2, 4, 6};
+ * V3 = __builtin_shuffle(V1, V2, M);
+ * ```
+ */
+class BuiltInOperationBuiltInShuffle extends BuiltInOperation, @builtinshuffle {
+ override string toString() { result = "__builtin_shuffle" }
+
+ override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInShuffle" }
+}
+
/**
* A clang `__builtin_convertvector` expression.
*
* Allows for conversion of vectors of equal element count and compatible
- * element types. Please see
+ * element types. See
* https://releases.llvm.org/3.7.0/tools/clang/docs/LanguageExtensions.html#builtin-convertvector
* for more information.
* ```
@@ -547,7 +564,7 @@ class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation,
* ```
* template
* struct is_trivially_constructible
- * : public integral_constant
+ * : public integral_constant
* { };
* ```
*/
@@ -612,13 +629,10 @@ class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istrivi
* The `__is_trivially_assignable` built-in operation (used by some
* implementations of the `` header).
*
- * Returns `true` if the assignment operator `C::operator =(const C& c)` is
- * trivial.
+ * Returns `true` if the assignment operator `C::operator =(const D& d)` is
+ * trivial (i.e., it will not call any operation that is non-trivial).
* ```
- * template
- * struct is_trivially_assignable
- * : public integral_constant
- * { };
+ * bool v = __is_trivially_assignable(MyType1, MyType2);
* ```
*/
class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istriviallyassignableexpr {
@@ -631,10 +645,10 @@ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istrivial
* The `__is_nothrow_assignable` built-in operation (used by some
* implementations of the `` header).
*
- * Returns true if there exists a `C::operator =(const C& c) nothrow`
+ * Returns true if there exists a `C::operator =(const D& d) nothrow`
* assignment operator (i.e, with an empty exception specification).
* ```
- * bool v = __is_nothrow_assignable(MyType);
+ * bool v = __is_nothrow_assignable(MyType1, MyType2);
* ```
*/
class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowassignableexpr {
@@ -643,15 +657,30 @@ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowas
override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowAssignable" }
}
+/**
+ * The `__is_assignable` built-in operation (used by some implementations
+ * of the `` header).
+ *
+ * Returns true if there exists a `C::operator =(const D& d)` assignment
+ * operator.
+ * ```
+ * bool v = __is_assignable(MyType1, MyType2);
+ * ```
+ */
+class BuiltInOperationIsAssignable extends BuiltInOperation, @isassignable {
+ override string toString() { result = "__is_assignable" }
+
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsAssignable" }
+}
+
/**
* The `__is_standard_layout` built-in operation (used by some implementations
* of the `` header).
*
* Returns `true` if the type is a primitive type, or a `class`, `struct` or
- * `union` WITHOUT (1) virtual functions or base classes, (2) reference member
- * variable or (3) multiple occurrences of base `class` objects, among other
- * restrictions. Please see
- * https://en.cppreference.com/w/cpp/named_req/StandardLayoutType
+ * `union` without (1) virtual functions or base classes, (2) reference member
+ * variable, or (3) multiple occurrences of base `class` objects, among other
+ * restrictions. See https://en.cppreference.com/w/cpp/named_req/StandardLayoutType
* for more information.
* ```
* bool v = __is_standard_layout(MyType);
@@ -668,7 +697,7 @@ class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayo
* implementations of the `` header).
*
* Returns `true` if instances of this type can be copied by trivial
- * means. The copying is done in a manner similar to the `memcpy`
+ * means. The copying is done in a manner similar to the `memcpy`
* function.
*/
class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istriviallycopyableexpr {
@@ -682,13 +711,13 @@ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istrivially
* the `` header).
*
* Returns `true` if the type is a scalar type, a reference type or an array of
- * literal types, among others. Please see
+ * literal types, among others. See
* https://en.cppreference.com/w/cpp/named_req/LiteralType
* for more information.
*
* ```
* template
- * std::integral_constant< bool, __is_literal_type(_Tp)> ilt;
+ * std::integral_constant ilt;
* ```
*/
class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr {
@@ -705,7 +734,7 @@ class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr
* compiler, with semantics of the `memcpy` operation.
* ```
* template
- * std::integral_constant< bool, __has_trivial_move_constructor(_Tp)> htmc;
+ * std::integral_constant htmc;
* ```
*/
class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation,
@@ -723,7 +752,7 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation,
* ```
* template
* struct has_trivial_move_assign
- * : public integral_constant
+ * : public integral_constant
* { };
* ```
*/
@@ -758,7 +787,7 @@ class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrow
* ```
* template
* struct is_constructible
- * : public integral_constant
+ * : public integral_constant
* { };
* ```
*/
@@ -785,7 +814,7 @@ class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothro
}
/**
- * The `__has_finalizer` built-in operation. This is a Microsoft extension.
+ * The `__has_finalizer` built-in operation. This is a Microsoft extension.
*
* Returns `true` if the type defines a _finalizer_ `C::!C(void)`, to be called
* from either the regular destructor or the garbage collector.
@@ -800,10 +829,10 @@ class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr {
}
/**
- * The `__is_delegate` built-in operation. This is a Microsoft extension.
+ * The `__is_delegate` built-in operation. This is a Microsoft extension.
*
* Returns `true` if the function has been declared as a `delegate`, used in
- * message forwarding. Please see
+ * message forwarding. See
* https://docs.microsoft.com/en-us/cpp/extensions/delegate-cpp-component-extensions
* for more information.
*/
@@ -814,9 +843,9 @@ class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr {
}
/**
- * The `__is_interface_class` built-in operation. This is a Microsoft extension.
+ * The `__is_interface_class` built-in operation. This is a Microsoft extension.
*
- * Returns `true` if the type has been declared as an `interface`. Please see
+ * Returns `true` if the type has been declared as an `interface`. See
* https://docs.microsoft.com/en-us/cpp/extensions/interface-class-cpp-component-extensions
* for more information.
*/
@@ -827,9 +856,9 @@ class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfacecla
}
/**
- * The `__is_ref_array` built-in operation. This is a Microsoft extension.
+ * The `__is_ref_array` built-in operation. This is a Microsoft extension.
*
- * Returns `true` if the object passed in is a _platform array_. Please see
+ * Returns `true` if the object passed in is a _platform array_. See
* https://docs.microsoft.com/en-us/cpp/extensions/arrays-cpp-component-extensions
* for more information.
* ```
@@ -844,9 +873,9 @@ class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr {
}
/**
- * The `__is_ref_class` built-in operation. This is a Microsoft extension.
+ * The `__is_ref_class` built-in operation. This is a Microsoft extension.
*
- * Returns `true` if the type is a _reference class_. Please see
+ * Returns `true` if the type is a _reference class_. See
* https://docs.microsoft.com/en-us/cpp/extensions/classes-and-structs-cpp-component-extensions
* for more information.
* ```
@@ -861,10 +890,10 @@ class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr {
}
/**
- * The `__is_sealed` built-in operation. This is a Microsoft extension.
+ * The `__is_sealed` built-in operation. This is a Microsoft extension.
*
* Returns `true` if a given class or virtual function is marked as `sealed`,
- * meaning that it cannot be extended or overridden. The `sealed` keyword
+ * meaning that it cannot be extended or overridden. The `sealed` keyword
* is similar to the C++11 `final` keyword.
* ```
* ref class X sealed {
@@ -879,7 +908,7 @@ class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr {
}
/**
- * The `__is_simple_value_class` built-in operation. This is a Microsoft extension.
+ * The `__is_simple_value_class` built-in operation. This is a Microsoft extension.
*
* Returns `true` if passed a value type that contains no references to the
* garbage-collected heap.
@@ -898,9 +927,9 @@ class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalu
}
/**
- * The `__is_value_class` built-in operation. This is a Microsoft extension.
+ * The `__is_value_class` built-in operation. This is a Microsoft extension.
*
- * Returns `true` if passed a value type. Please see
+ * Returns `true` if passed a value type. See
* https://docs.microsoft.com/en-us/cpp/extensions/classes-and-structs-cpp-component-extensions
* For more information.
* ```
@@ -922,7 +951,7 @@ class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr {
* ```
* template
* struct is_final
- * : public integral_constant
+ * : public integral_constant
* { };
* ```
*/
@@ -933,7 +962,7 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr {
}
/**
- * The `__builtin_choose_expr` expression. This is a GNU/Clang extension.
+ * The `__builtin_choose_expr` expression. This is a gcc/clang extension.
*
* The expression functions similarly to the ternary `?:` operator, except
* that it is evaluated at compile-time.
@@ -978,3 +1007,50 @@ class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex {
/** Gets the operand corresponding to the imaginary part of the complex number. */
Expr getImaginaryOperand() { this.hasChild(result, 1) }
}
+
+/**
+ * A C++ `__is_aggregate` built-in operation (used by some implementations of the
+ * `` header).
+ *
+ * Returns `true` if the type has is an aggregate type.
+ * ```
+ * std::integral_constant ia;
+ * ```
+ */
+class BuiltInOperationIsAggregate extends BuiltInOperation, @isaggregate {
+ override string toString() { result = "__is_aggregate" }
+
+ override string getAPrimaryQlClass() { result = "BuiltInOperationIsAggregate" }
+}
+
+/**
+ * A C++ `__has_unique_object_representations` built-in operation (used by some
+ * implementations of the `` header).
+ *
+ * Returns `true` if the type is trivially copyable and if the object representation
+ * is unique for two objects with the same value.
+ * ```
+ * bool v = __has_unique_object_representations(MyType);
+ * ```
+ */
+class BuiltInOperationHasUniqueObjectRepresentations extends BuiltInOperation,
+ @hasuniqueobjectrepresentations {
+ override string toString() { result = "__has_unique_object_representations" }
+
+ override string getAPrimaryQlClass() { result = "BuiltInOperationHasUniqueObjectRepresentations" }
+}
+
+/**
+ * A C/C++ `__builtin_bit_cast` built-in operation (used by some implementations
+ * of `std::bit_cast`).
+ *
+ * Performs a bit cast from a value to a type.
+ * ```
+ * __builtin_bit_cast(Type, value);
+ * ```
+ */
+class BuiltInBitCast extends BuiltInOperation, @builtinbitcast {
+ override string toString() { result = "__builtin_bit_cast" }
+
+ override string getAPrimaryQlClass() { result = "BuiltInBitCast" }
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll
index 7ceda8ddbff..dba3d16997f 100644
--- a/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll
+++ b/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll
@@ -255,8 +255,10 @@ class FunctionCall extends Call, @funbindexpr {
/**
* Gets the function called by this call.
*
- * In the case of virtual function calls, the result is the most-specific function in the override tree (as
- * determined by the compiler) such that the target at runtime will be one of `result.getAnOverridingFunction*()`.
+ * In the case of virtual function calls, the result is the most-specific function in the override tree
+ * such that the target at runtime will be one of `result.getAnOverridingFunction*()`. The most-specific
+ * function is determined by the compiler based on the compile time type of the object the function is a
+ * member of.
*/
override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) }
diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
index 55a59cc9588..68973293425 100644
--- a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
+++ b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
@@ -596,9 +596,12 @@ class ParenthesisExpr extends Conversion, @parexpr {
}
/**
- * A C/C++ expression that has not been resolved.
+ * A C/C++ expression that could not be resolved, or that can no longer be
+ * represented due to a database upgrade or downgrade.
*
- * It is assigned `ErroneousType` as its type.
+ * If the expression could not be resolved, it has type `ErroneousType`. In the
+ * case of a database upgrade or downgrade, the original type from before the
+ * upgrade or downgrade is kept if that type can be represented.
*/
class ErrorExpr extends Expr, @errorexpr {
override string toString() { result = "" }
diff --git a/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll b/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll
new file mode 100644
index 00000000000..c11e1457dae
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll
@@ -0,0 +1,57 @@
+private predicate hasDefinition(@globalvariable g) {
+ exists(@var_decl vd | var_decls(vd, g, _, _, _) | var_def(vd))
+}
+
+private predicate onlyOneCompleteGlobalVariableExistsWithMangledName(@mangledname name) {
+ strictcount(@globalvariable g | hasDefinition(g) and mangled_name(g, name)) = 1
+}
+
+/** Holds if `g` is a unique global variable with a definition named `name`. */
+private predicate isGlobalWithMangledNameAndWithDefinition(@mangledname name, @globalvariable g) {
+ hasDefinition(g) and
+ mangled_name(g, name) and
+ onlyOneCompleteGlobalVariableExistsWithMangledName(name)
+}
+
+/** Holds if `g` is a global variable without a definition named `name`. */
+private predicate isGlobalWithMangledNameAndWithoutDefinition(@mangledname name, @globalvariable g) {
+ not hasDefinition(g) and
+ mangled_name(g, name)
+}
+
+/**
+ * Holds if `incomplete` is a global variable without a definition, and there exists
+ * a unique global variable `complete` with the same name that does have a definition.
+ */
+private predicate hasTwinWithDefinition(@globalvariable incomplete, @globalvariable complete) {
+ exists(@mangledname name |
+ not variable_instantiation(incomplete, complete) and
+ isGlobalWithMangledNameAndWithoutDefinition(name, incomplete) and
+ isGlobalWithMangledNameAndWithDefinition(name, complete)
+ )
+}
+
+import Cached
+
+cached
+private module Cached {
+ /**
+ * If `v` is a global variable without a definition, and there exists a unique
+ * global variable with the same name that does have a definition, then the
+ * result is that unique global variable. Otherwise, the result is `v`.
+ */
+ cached
+ @variable resolveGlobalVariable(@variable v) {
+ hasTwinWithDefinition(v, result)
+ or
+ not hasTwinWithDefinition(v, _) and
+ result = v
+ }
+
+ cached
+ predicate isVariable(@variable v) {
+ not v instanceof @globalvariable
+ or
+ v = resolveGlobalVariable(_)
+ }
+}
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 1f3ea2a4d3d..08ee06acdda 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll
@@ -38,6 +38,9 @@ abstract class MustFlowConfiguration extends string {
*/
predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() }
+ /** Holds if this configuration allows flow from arguments to parameters. */
+ predicate allowInterproceduralFlow() { any() }
+
/**
* Holds if data must flow from `source` to `sink` for this configuration.
*
@@ -204,10 +207,25 @@ private module Cached {
}
}
+/**
+ * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this
+ * predicate ensures that joins go from `n` to the result instead of the other
+ * way around.
+ */
+pragma[inline]
+private Declaration getEnclosingCallable(DataFlow::Node n) {
+ pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingCallable()
+}
+
/** Holds if `nodeFrom` flows to `nodeTo`. */
private predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, MustFlowConfiguration config) {
exists(config) and
- Cached::step(nodeFrom, nodeTo)
+ Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and
+ (
+ config.allowInterproceduralFlow()
+ or
+ getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo)
+ )
or
config.isAdditionalFlowStep(nodeFrom, nodeTo)
}
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 7b9e78d2c4b..340bfe280b7 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
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
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 7b9e78d2c4b..340bfe280b7 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
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
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 7b9e78d2c4b..340bfe280b7 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
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
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 7b9e78d2c4b..340bfe280b7 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
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
index 9dcd7f176df..e035df824e2 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll
@@ -244,7 +244,25 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) {
* calling context. For example, this would happen with flow through a
* global or static variable.
*/
-predicate jumpStep(Node n1, Node n2) { none() }
+predicate jumpStep(Node n1, Node n2) {
+ exists(GlobalOrNamespaceVariable v |
+ v =
+ n1.asInstruction()
+ .(StoreInstruction)
+ .getResultAddress()
+ .(VariableAddressInstruction)
+ .getAstVariable() and
+ v = n2.asVariable()
+ or
+ v =
+ n2.asInstruction()
+ .(LoadInstruction)
+ .getSourceAddress()
+ .(VariableAddressInstruction)
+ .getAstVariable() and
+ v = n1.asVariable()
+ )
+}
/**
* Holds if data can flow from `node1` to `node2` via an assignment to `f`.
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll
index 103b5424197..38a886746ab 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll
@@ -947,7 +947,7 @@ abstract class TranslatedElement extends TTranslatedElement {
}
/**
- * Represents the IR translation of a root element, either a function or a global variable.
+ * The IR translation of a root element, either a function or a global variable.
*/
abstract class TranslatedRootElement extends TranslatedElement {
TranslatedRootElement() {
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll
index 31174d8ba5f..dde5e00361a 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll
@@ -65,7 +65,7 @@ class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement,
result = this.getInstruction(InitializerVariableAddressTag())
or
tag = InitializerVariableAddressTag() and
- result = getChild(1).getFirstInstruction()
+ result = this.getChild(1).getFirstInstruction()
or
tag = ReturnTag() and
result = this.getInstruction(AliasedUseTag())
diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Allocation.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Allocation.qll
index 892673ff1c8..325f6a6470b 100644
--- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Allocation.qll
+++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Allocation.qll
@@ -218,7 +218,7 @@ private class CallAllocationExpr extends AllocationExpr, FunctionCall {
exists(target.getReallocPtrArg()) and
this.getArgument(target.getSizeArg()).getValue().toInt() = 0
) and
- // these are modelled directly (and more accurately), avoid duplication
+ // these are modeled directly (and more accurately), avoid duplication
not exists(NewOrNewArrayExpr new | new.getAllocatorCall() = this)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
index c9e790d9077..0f47770dc82 100644
--- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
+++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll
@@ -1573,7 +1573,7 @@ private module SimpleRangeAnalysisCached {
result = min([max(getTruncatedUpperBounds(expr)), getGuardedUpperBound(expr)])
}
- /** Holds if the upper bound of `expr` may have been widened. This means the the upper bound is in practice likely to be overly wide. */
+ /** Holds if the upper bound of `expr` may have been widened. This means the upper bound is in practice likely to be overly wide. */
cached
predicate upperBoundMayBeWidened(Expr e) {
isRecursiveExpr(e) and
diff --git a/cpp/ql/lib/semmle/code/cpp/security/Overflow.qll b/cpp/ql/lib/semmle/code/cpp/security/Overflow.qll
index 4d6d22c086a..cf0d633cb6f 100644
--- a/cpp/ql/lib/semmle/code/cpp/security/Overflow.qll
+++ b/cpp/ql/lib/semmle/code/cpp/security/Overflow.qll
@@ -50,7 +50,7 @@ VariableAccess varUse(LocalScopeVariable v) { result = v.getAnAccess() }
* Holds if `e` potentially overflows and `use` is an operand of `e` that is not guarded.
*/
predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
- // Since `e` is guarenteed to be a `BinaryArithmeticOperation`, a `UnaryArithmeticOperation` or
+ // Since `e` is guaranteed to be a `BinaryArithmeticOperation`, a `UnaryArithmeticOperation` or
// an `AssignArithmeticOperation` by the other constraints in this predicate, we know that
// `convertedExprMightOverflowPositively` will have a result even when `e` is not analyzable
// by `SimpleRangeAnalysis`.
@@ -80,7 +80,7 @@ predicate missingGuardAgainstOverflow(Operation e, VariableAccess use) {
* Holds if `e` potentially underflows and `use` is an operand of `e` that is not guarded.
*/
predicate missingGuardAgainstUnderflow(Operation e, VariableAccess use) {
- // Since `e` is guarenteed to be a `BinaryArithmeticOperation`, a `UnaryArithmeticOperation` or
+ // Since `e` is guaranteed to be a `BinaryArithmeticOperation`, a `UnaryArithmeticOperation` or
// an `AssignArithmeticOperation` by the other constraints in this predicate, we know that
// `convertedExprMightOverflowNegatively` will have a result even when `e` is not analyzable
// by `SimpleRangeAnalysis`.
diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme
index 19e31bf071f..34549c3b093 100644
--- a/cpp/ql/lib/semmlecode.cpp.dbscheme
+++ b/cpp/ql/lib/semmlecode.cpp.dbscheme
@@ -899,6 +899,7 @@ case @attribute_arg.kind of
| 1 = @attribute_arg_token
| 2 = @attribute_arg_constant
| 3 = @attribute_arg_type
+| 4 = @attribute_arg_constant_expr
;
attribute_arg_value(
@@ -909,6 +910,10 @@ attribute_arg_type(
unique int arg: @attribute_arg ref,
int type_id: @type ref
);
+attribute_arg_constant(
+ unique int arg: @attribute_arg ref,
+ int constant: @expr ref
+)
attribute_arg_name(
unique int arg: @attribute_arg ref,
string name: string ref
@@ -1650,6 +1655,11 @@ case @expr.kind of
| 327 = @co_await
| 328 = @co_yield
| 329 = @temp_init
+| 330 = @isassignable
+| 331 = @isaggregate
+| 332 = @hasuniqueobjectrepresentations
+| 333 = @builtinbitcast
+| 334 = @builtinshuffle
;
@var_args_expr = @vastartexpr
@@ -1711,6 +1721,11 @@ case @expr.kind of
| @isfinalexpr
| @builtinchooseexpr
| @builtincomplex
+ | @isassignable
+ | @isaggregate
+ | @hasuniqueobjectrepresentations
+ | @builtinbitcast
+ | @builtinshuffle
;
new_allocated_type(
diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme.stats b/cpp/ql/lib/semmlecode.cpp.dbscheme.stats
index e439c5ac624..6ab07086478 100644
--- a/cpp/ql/lib/semmlecode.cpp.dbscheme.stats
+++ b/cpp/ql/lib/semmlecode.cpp.dbscheme.stats
@@ -2,7 +2,11 @@
@compilation
- 9980
+ 9948
+
+
+ @external_package
+ 4
@externalDataElement
@@ -12,73 +16,69 @@
@svnentry
575525
-
- @external_package
- 4
-
@location_stmt
- 3805462
+ 3813503
@diagnostic
- 582773
+ 72092
@file
- 124189
+ 122193
@folder
- 15994
-
-
- @location_expr
- 13128112
+ 15274
@location_default
- 30128761
+ 29655056
+
+
+ @location_expr
+ 13165921
@macroinvocation
- 33894981
+ 33818017
@function
- 4726273
+ 4628077
@fun_decl
- 5096962
+ 4995583
@type_decl
- 3283977
+ 3240440
@namespace_decl
- 306972
+ 307432
@using
- 374921
+ 369357
@static_assert
- 130417
+ 130562
@var_decl
- 8543232
+ 8391080
@parameter
- 6660155
+ 6536424
@membervariable
- 1050083
+ 1052962
@globalvariable
@@ -86,63 +86,63 @@
@localvariable
- 581690
+ 581169
@enumconstant
- 240613
+ 241273
@builtintype
- 22109
+ 21754
@derivedtype
- 4413446
+ 4324907
@decltype
- 31047
+ 28696
@usertype
- 5342989
+ 5230250
@mangledname
- 1728010
+ 2114402
@type_mention
- 4011508
+ 4022510
@routinetype
- 546982
+ 547156
@ptrtomember
- 38103
+ 37491
@specifier
- 24932
+ 24531
@gnuattribute
- 664228
+ 653549
@stdattribute
- 468982
+ 468565
@alignas
- 8937
+ 8794
@declspec
- 238544
+ 239199
@msattribute
@@ -150,135 +150,139 @@
@attribute_arg_token
- 25402
-
-
- @attribute_arg_constant
- 326469
+ 24994
@attribute_arg_type
- 470
+ 462
+
+
+ @attribute_arg_constant_expr
+ 322609
@attribute_arg_empty
1
+
+ @attribute_arg_constant
+ 1
+
@derivation
- 402388
+ 368264
@frienddecl
- 715075
+ 716133
@comment
- 8781270
+ 8773472
@namespace
- 12701
+ 12497
@specialnamequalifyingelement
- 470
+ 462
@namequalifier
- 1618961
+ 1536058
@value
- 10646146
+ 10759270
@initialiser
- 1731824
+ 1733596
@lambdacapture
- 28224
+ 27771
@stmt_expr
- 1480414
+ 1483542
@stmt_if
- 723174
+ 724702
@stmt_while
- 30207
+ 30109
@stmt_label
- 53046
+ 53054
@stmt_return
- 1306346
+ 1285345
@stmt_block
- 1446530
+ 1423276
@stmt_end_test_while
- 148604
+ 148628
@stmt_for
- 61324
+ 61453
@stmt_switch_case
- 191408
+ 209699
@stmt_switch
- 20901
+ 20747
@stmt_try_block
- 42701
+ 46934
@stmt_decl
- 608508
+ 606536
@stmt_empty
- 193484
+ 193311
@stmt_continue
- 22507
+ 22525
@stmt_break
- 102231
+ 102345
@stmt_range_based_for
- 8467
+ 8331
@stmt_handler
- 59432
+ 65331
@stmt_constexpr_if
- 52551
+ 52504
@stmt_goto
- 110490
+ 110508
@stmt_asm
- 110589
+ 109773
@stmt_microsoft_try
@@ -290,11 +294,11 @@
@stmt_vla_decl
- 21
+ 22
@stmt_assigned_goto
- 9059
+ 9060
@stmt_co_return
@@ -302,71 +306,71 @@
@delete_array_expr
- 1410
+ 1406
@new_array_expr
- 5099
+ 5104
@ctordirectinit
- 112813
+ 112980
@ctorvirtualinit
- 6534
+ 6512
@ctorfieldinit
- 200789
+ 201086
@ctordelegatinginit
- 3347
+ 3352
@dtordirectdestruct
- 41715
+ 41776
@dtorvirtualdestruct
- 4122
+ 4128
@dtorfielddestruct
- 41644
+ 41706
@static_cast
- 211821
+ 210928
@reinterpret_cast
- 30776
+ 30749
@const_cast
- 35278
+ 35247
@dynamic_cast
- 1040
+ 1037
@c_style_cast
- 4209393
+ 4209396
@lambdaexpr
- 21639
+ 21291
@param_ref
- 375289
+ 244969
@errorexpr
- 46823
+ 46893
@address_of
@@ -374,27 +378,27 @@
@reference_to
- 1596143
+ 1592318
@indirect
- 292115
+ 292162
@ref_indirect
- 1934537
+ 1938635
@array_to_pointer
- 1424883
+ 1428562
@vacuous_destructor_call
- 8138
+ 8150
@parexpr
- 3572547
+ 3581771
@arithnegexpr
@@ -402,111 +406,111 @@
@complementexpr
- 27787
+ 27791
@notexpr
- 277992
+ 275941
@postincrexpr
- 61774
+ 61943
@postdecrexpr
- 41860
+ 41968
@preincrexpr
- 70307
+ 70456
@predecrexpr
- 26108
+ 26164
@conditionalexpr
- 654502
+ 656192
@addexpr
- 400358
+ 397789
@subexpr
- 339340
+ 340216
@mulexpr
- 305891
+ 305921
@divexpr
- 133731
+ 132913
@remexpr
- 15819
+ 15842
@paddexpr
- 86505
+ 86519
@psubexpr
- 49681
+ 49818
@pdiffexpr
- 35487
+ 35456
@lshiftexpr
- 562988
+ 565473
@rshiftexpr
- 141617
+ 140572
@andexpr
- 491818
+ 488189
@orexpr
- 146267
+ 145188
@xorexpr
- 53938
+ 54086
@eqexpr
- 468873
+ 469864
@neexpr
- 300411
+ 301187
@gtexpr
- 100669
+ 99050
@ltexpr
- 106314
+ 104605
@geexpr
- 58989
+ 59151
@leexpr
- 212141
+ 212175
@assignexpr
- 933419
+ 935391
@assignaddexpr
@@ -514,15 +518,15 @@
@assignsubexpr
- 11157
+ 11180
@assignmulexpr
- 7170
+ 7146
@assigndivexpr
- 4972
+ 4985
@assignremexpr
@@ -530,7 +534,7 @@
@assignlshiftexpr
- 2704
+ 2712
@assignrshiftexpr
@@ -538,119 +542,119 @@
@assignandexpr
- 4852
+ 4817
@assignorexpr
- 23850
+ 23829
@assignxorexpr
- 21804
+ 21807
@assignpaddexpr
- 13577
+ 13606
@andlogicalexpr
- 249008
+ 249535
@orlogicalexpr
- 864018
+ 864675
@commaexpr
- 181539
+ 124044
@subscriptexpr
- 367910
+ 367580
@callexpr
- 309063
+ 304095
@vastartexpr
- 3703
+ 3707
@vaendexpr
- 2822
+ 2777
@vacopyexpr
- 140
+ 141
@varaccess
- 6006364
+ 6019055
@thisaccess
- 1125914
+ 1127165
@new_expr
- 47598
+ 47669
@delete_expr
- 11732
+ 11749
@throw_expr
- 21765
+ 21694
@condition_decl
- 38595
+ 42438
@braced_init_list
- 1008
+ 1108
@type_id
- 36430
+ 36484
@runtime_sizeof
- 289268
+ 295484
@runtime_alignof
- 48550
+ 49892
@sizeof_pack
- 5644
+ 5554
@routineexpr
- 3047613
+ 2917668
@type_operand
- 1126029
+ 1126886
@isemptyexpr
- 2303
+ 1481
@ispodexpr
- 636
+ 634
@hastrivialdestructor
- 470
+ 462
@literal
- 4350894
+ 4406598
@aggregateliteral
@@ -658,39 +662,39 @@
@istrivialexpr
- 940
+ 925
@istriviallycopyableexpr
- 3763
+ 3702
@isconstructibleexpr
- 2721
+ 462
@isfinalexpr
- 2093
+ 1693
@noexceptexpr
- 23574
+ 25724
@builtinaddressof
- 13282
+ 13302
@temp_init
- 760683
+ 826875
@assume
- 3200
+ 3203
@unaryplusexpr
- 2903
+ 2911
@conjugation
@@ -738,7 +742,7 @@
@assignpsubexpr
- 1148
+ 1150
@virtfunptrexpr
@@ -750,11 +754,11 @@
@expr_stmt
- 94929
+ 94228
@offsetofexpr
- 20103
+ 19954
@hasassignexpr
@@ -798,7 +802,7 @@
@isabstractexpr
- 18
+ 19
@isbaseofexpr
@@ -806,15 +810,15 @@
@isclassexpr
- 1835
+ 1837
@isconvtoexpr
- 1151
+ 104
@isenumexpr
- 1256
+ 522
@ispolyexpr
@@ -826,7 +830,7 @@
@typescompexpr
- 562415
+ 562843
@intaddrexpr
@@ -834,7 +838,7 @@
@uuidof
- 19993
+ 20022
@foldexpr
@@ -846,7 +850,7 @@
@istriviallyconstructibleexpr
- 2826
+ 732
@isdestructibleexpr
@@ -858,19 +862,19 @@
@istriviallydestructibleexpr
- 5
+ 836
@istriviallyassignableexpr
- 523
+ 3
@isnothrowassignableexpr
- 2093
+ 4183
@isstandardlayoutexpr
- 837
+ 2
@isliteraltypeexpr
@@ -890,7 +894,7 @@
@isnothrowconstructibleexpr
- 4815
+ 14433
@hasfinalizerexpr
@@ -930,7 +934,7 @@
@builtinchooseexpr
- 9136
+ 9069
@vec_fill
@@ -956,53 +960,73 @@
@co_yield
1
+
+ @isassignable
+ 3
+
+
+ @isaggregate
+ 2
+
+
+ @hasuniqueobjectrepresentations
+ 2
+
+
+ @builtinbitcast
+ 1
+
+
+ @builtinshuffle
+ 1959
+
@ppd_if
- 672225
+ 661418
@ppd_ifdef
- 265314
+ 261049
@ppd_ifndef
- 268607
+ 264289
@ppd_elif
- 25402
+ 24994
@ppd_else
- 210746
+ 207358
@ppd_endif
- 1206147
+ 1186757
@ppd_plain_include
- 313767
+ 308723
@ppd_define
- 2435252
+ 2433089
@ppd_undef
- 262021
+ 257809
@ppd_pragma
- 312270
+ 311993
@ppd_include_next
- 1881
+ 1851
@ppd_line
- 27780
+ 27755
@ppd_error
@@ -1018,7 +1042,7 @@
@link_target
- 1471
+ 1475
@xmldtd
@@ -1048,11 +1072,11 @@
compilations
- 9980
+ 9948
id
- 9980
+ 9948
cwd
@@ -1070,7 +1094,7 @@
1
2
- 9980
+ 9948
@@ -1096,19 +1120,19 @@
compilation_args
- 656151
+ 651309
id
- 5544
+ 5503
num
- 713
+ 707
arg
- 34651
+ 34395
@@ -1122,17 +1146,17 @@
23
69
- 489
+ 485
71
102
- 276
+ 274
126
127
- 3889
+ 3861
127
@@ -1142,7 +1166,7 @@
131
132
- 819
+ 813
134
@@ -1163,17 +1187,17 @@
23
57
- 489
+ 485
57
106
- 292
+ 290
106
107
- 3852
+ 3824
107
@@ -1183,7 +1207,7 @@
109
110
- 819
+ 813
111
@@ -1209,7 +1233,7 @@
898
899
- 133
+ 132
911
@@ -1229,12 +1253,12 @@
970
989
- 37
+ 36
999
1000
- 74
+ 73
1001
@@ -1254,7 +1278,7 @@
1042
1043
- 122
+ 121
@@ -1285,7 +1309,7 @@
8
13
- 53
+ 52
13
@@ -1330,7 +1354,7 @@
169
819
- 53
+ 52
@@ -1346,12 +1370,12 @@
1
2
- 32576
+ 32335
2
1043
- 2075
+ 2059
@@ -1367,12 +1391,12 @@
1
2
- 33438
+ 33191
2
56
- 1213
+ 1204
@@ -1382,19 +1406,19 @@
compilation_compiling_files
- 11495
+ 11527
id
- 1988
+ 1994
num
- 3301
+ 3310
file
- 9984
+ 10011
@@ -1408,7 +1432,7 @@
1
2
- 994
+ 997
2
@@ -1423,7 +1447,7 @@
4
5
- 238
+ 239
5
@@ -1459,7 +1483,7 @@
1
2
- 994
+ 997
2
@@ -1474,7 +1498,7 @@
4
5
- 238
+ 239
5
@@ -1510,27 +1534,27 @@
1
2
- 1750
+ 1755
2
3
- 715
+ 717
3
4
- 357
+ 358
4
13
- 278
+ 279
13
51
- 198
+ 199
@@ -1546,27 +1570,27 @@
1
2
- 1750
+ 1755
2
3
- 715
+ 717
3
4
- 357
+ 358
4
13
- 278
+ 279
13
49
- 198
+ 199
@@ -1582,12 +1606,12 @@
1
2
- 8989
+ 9014
2
4
- 835
+ 837
4
@@ -1608,12 +1632,12 @@
1
2
- 9148
+ 9173
2
4
- 795
+ 797
4
@@ -1628,15 +1652,15 @@
compilation_time
- 45982
+ 46108
id
- 1988
+ 1994
num
- 3301
+ 3310
kind
@@ -1644,7 +1668,7 @@
seconds
- 12490
+ 14239
@@ -1658,7 +1682,7 @@
1
2
- 994
+ 997
2
@@ -1673,7 +1697,7 @@
4
5
- 238
+ 239
5
@@ -1709,7 +1733,7 @@
4
5
- 1988
+ 1994
@@ -1725,47 +1749,47 @@
3
4
- 198
+ 279
4
5
- 795
+ 717
- 5
+ 6
9
159
9
+ 10
+ 79
+
+
+ 10
11
119
11
12
- 198
+ 159
- 13
+ 14
18
159
18
22
- 119
-
-
- 22
- 46
159
- 60
- 104
- 79
+ 24
+ 124
+ 159
@@ -1781,27 +1805,27 @@
1
2
- 1750
+ 1755
2
3
- 715
+ 717
3
4
- 357
+ 358
4
13
- 278
+ 279
13
51
- 198
+ 199
@@ -1817,7 +1841,7 @@
4
5
- 3301
+ 3310
@@ -1833,47 +1857,47 @@
3
4
- 556
+ 598
4
5
- 1233
+ 1156
5
6
- 238
+ 199
6
7
- 119
+ 319
7
8
- 318
+ 199
8
9
- 119
+ 159
9
- 11
- 278
+ 12
+ 279
- 11
- 31
- 278
+ 13
+ 41
+ 279
- 40
- 95
- 159
+ 41
+ 96
+ 119
@@ -1924,13 +1948,13 @@
79
- 152
- 153
+ 177
+ 178
39
- 160
- 161
+ 179
+ 180
39
@@ -1947,22 +1971,22 @@
1
2
- 7875
+ 9373
2
3
- 2704
+ 3510
3
- 4
- 1312
+ 5
+ 1196
- 4
- 45
- 596
+ 5
+ 46
+ 159
@@ -1978,27 +2002,22 @@
1
2
- 6682
+ 8735
2
3
- 3142
+ 3589
3
4
- 1431
+ 1116
4
- 6
- 954
-
-
- 6
- 65
- 278
+ 74
+ 797
@@ -2014,12 +2033,12 @@
1
2
- 12251
+ 14000
2
- 3
- 238
+ 4
+ 239
@@ -2029,23 +2048,23 @@
diagnostic_for
- 1031027
+ 889095
diagnostic
- 582773
+ 72092
compilation
- 1988
+ 9556
file_number
- 104
+ 11
file_number_diagnostic_number
- 104788
+ 6835
@@ -2059,27 +2078,17 @@
1
2
- 362937
+ 9602
2
3
- 92540
+ 59654
- 3
- 4
- 70033
-
-
- 4
- 6
- 47421
-
-
- 6
- 9
- 9840
+ 254
+ 825
+ 2835
@@ -2095,7 +2104,7 @@
1
2
- 582773
+ 72092
@@ -2111,22 +2120,7 @@
1
2
- 475263
-
-
- 2
- 3
- 50143
-
-
- 3
- 6
- 53702
-
-
- 6
- 7
- 3663
+ 72092
@@ -2135,222 +2129,6 @@
compilation
diagnostic
-
-
- 12
-
-
- 32
- 33
- 104
-
-
- 37
- 38
- 104
-
-
- 77
- 78
- 104
-
-
- 78
- 79
- 104
-
-
- 155
- 156
- 104
-
-
- 359
- 360
- 104
-
-
- 418
- 419
- 104
-
-
- 436
- 437
- 104
-
-
- 509
- 510
- 104
-
-
- 571
- 572
- 104
-
-
- 639
- 640
- 104
-
-
- 756
- 757
- 628
-
-
- 1001
- 1002
- 209
-
-
-
-
-
-
- compilation
- file_number
-
-
- 12
-
-
- 1
- 2
- 1988
-
-
-
-
-
-
- compilation
- file_number_diagnostic_number
-
-
- 12
-
-
- 32
- 33
- 104
-
-
- 37
- 38
- 104
-
-
- 77
- 78
- 104
-
-
- 78
- 79
- 104
-
-
- 155
- 156
- 104
-
-
- 359
- 360
- 104
-
-
- 418
- 419
- 104
-
-
- 436
- 437
- 104
-
-
- 509
- 510
- 104
-
-
- 571
- 572
- 104
-
-
- 639
- 640
- 104
-
-
- 756
- 757
- 628
-
-
- 1001
- 1002
- 209
-
-
-
-
-
-
- file_number
- diagnostic
-
-
- 12
-
-
- 5567
- 5568
- 104
-
-
-
-
-
-
- file_number
- compilation
-
-
- 12
-
-
- 19
- 20
- 104
-
-
-
-
-
-
- file_number
- file_number_diagnostic_number
-
-
- 12
-
-
- 1001
- 1002
- 104
-
-
-
-
-
-
- file_number_diagnostic_number
- diagnostic
12
@@ -2358,37 +2136,193 @@
2
3
- 25647
+ 57
7
8
- 10991
+ 6074
8
9
- 13085
+ 495
- 9
- 10
- 13608
+ 247
+ 248
+ 1959
- 10
- 11
- 27741
+ 263
+ 444
+ 760
- 11
- 12
- 5652
+ 446
+ 594
+ 207
+
+
+
+
+
+
+ compilation
+ file_number
+
+
+ 12
+
+
+ 1
+ 2
+ 9556
+
+
+
+
+
+
+ compilation
+ file_number_diagnostic_number
+
+
+ 12
+
+
+ 2
+ 3
+ 57
- 12
- 15
- 8060
+ 7
+ 8
+ 6074
+
+
+ 8
+ 9
+ 495
+
+
+ 247
+ 248
+ 1959
+
+
+ 263
+ 444
+ 760
+
+
+ 446
+ 594
+ 207
+
+
+
+
+
+
+ file_number
+ diagnostic
+
+
+ 12
+
+
+ 6254
+ 6255
+ 11
+
+
+
+
+
+
+ file_number
+ compilation
+
+
+ 12
+
+
+ 829
+ 830
+ 11
+
+
+
+
+
+
+ file_number
+ file_number_diagnostic_number
+
+
+ 12
+
+
+ 593
+ 594
+ 11
+
+
+
+
+
+
+ file_number_diagnostic_number
+ diagnostic
+
+
+ 12
+
+
+ 1
+ 2
+ 2812
+
+
+ 2
+ 5
+ 599
+
+
+ 5
+ 6
+ 1014
+
+
+ 7
+ 14
+ 541
+
+
+ 15
+ 16
+ 57
+
+
+ 17
+ 18
+ 599
+
+
+ 18
+ 23
+ 461
+
+
+ 26
+ 40
+ 553
+
+
+ 42
+ 830
+ 195
@@ -2402,49 +2336,54 @@
12
- 2
- 3
- 25647
-
-
- 8
+ 4
9
- 12247
-
-
- 9
- 10
- 7118
+ 587
10
11
- 6490
-
-
- 11
- 13
- 9526
-
-
- 13
- 14
- 6176
+ 1002
14
- 15
- 21355
+ 27
+ 541
- 15
- 16
- 8060
+ 30
+ 31
+ 57
- 16
- 20
- 8165
+ 34
+ 35
+ 599
+
+
+ 36
+ 45
+ 461
+
+
+ 52
+ 79
+ 553
+
+
+ 84
+ 85
+ 184
+
+
+ 254
+ 255
+ 2755
+
+
+ 297
+ 830
+ 92
@@ -2460,7 +2399,7 @@
1
2
- 104788
+ 6835
@@ -2470,19 +2409,19 @@
compilation_finished
- 9980
+ 9948
id
- 9980
+ 9948
cpu_seconds
- 7956
+ 7688
elapsed_seconds
- 138
+ 161
@@ -2496,7 +2435,7 @@
1
2
- 9980
+ 9948
@@ -2512,7 +2451,7 @@
1
2
- 9980
+ 9948
@@ -2528,17 +2467,17 @@
1
2
- 6777
+ 6363
2
3
- 786
+ 853
3
- 17
- 393
+ 15
+ 472
@@ -2554,12 +2493,12 @@
1
2
- 7505
+ 7123
2
3
- 451
+ 564
@@ -2572,39 +2511,115 @@
12
+
+ 1
+ 2
+ 23
+
2
3
+ 34
+
+
+ 6
+ 7
+ 11
+
+
+ 8
+ 9
+ 11
+
+
+ 12
+ 13
+ 11
+
+
+ 21
+ 22
+ 11
+
+
+ 46
+ 47
+ 11
+
+
+ 135
+ 136
+ 11
+
+
+ 158
+ 159
+ 11
+
+
+ 232
+ 233
+ 11
+
+
+ 237
+ 238
+ 11
+
+
+
+
+
+
+ elapsed_seconds
+ cpu_seconds
+
+
+ 12
+
+
+ 1
+ 2
23
- 3
- 4
- 23
+ 2
+ 3
+ 34
- 9
- 10
+ 6
+ 7
11
- 14
- 15
+ 8
+ 9
11
- 22
- 23
+ 12
+ 13
11
- 37
- 38
+ 21
+ 22
11
- 144
- 145
+ 45
+ 46
+ 11
+
+
+ 110
+ 111
+ 11
+
+
+ 122
+ 123
11
@@ -2617,72 +2632,6 @@
223
11
-
- 243
- 244
- 11
-
-
-
-
-
-
- elapsed_seconds
- cpu_seconds
-
-
- 12
-
-
- 2
- 3
- 23
-
-
- 3
- 4
- 23
-
-
- 9
- 10
- 11
-
-
- 14
- 15
- 11
-
-
- 22
- 23
- 11
-
-
- 36
- 37
- 11
-
-
- 118
- 119
- 11
-
-
- 123
- 124
- 11
-
-
- 177
- 178
- 11
-
-
- 218
- 219
- 11
-
@@ -2907,11 +2856,11 @@
sourceLocationPrefix
- 470
+ 462
prefix
- 470
+ 462
@@ -4405,31 +4354,31 @@
locations_default
- 30128761
+ 29655056
id
- 30128761
+ 29655056
container
- 140184
+ 137467
startLine
- 2114992
+ 2080991
startColumn
- 37162
+ 36565
endLine
- 2117814
+ 2083768
endColumn
- 48452
+ 47673
@@ -4443,7 +4392,7 @@
1
2
- 30128761
+ 29655056
@@ -4459,7 +4408,7 @@
1
2
- 30128761
+ 29655056
@@ -4475,7 +4424,7 @@
1
2
- 30128761
+ 29655056
@@ -4491,7 +4440,7 @@
1
2
- 30128761
+ 29655056
@@ -4507,7 +4456,7 @@
1
2
- 30128761
+ 29655056
@@ -4523,67 +4472,67 @@
1
2
- 16464
+ 15737
2
12
- 10819
+ 10645
13
20
- 11760
+ 11571
21
36
- 11289
+ 11108
36
55
- 11289
+ 11108
55
77
- 10819
+ 10645
77
102
- 10819
+ 10645
102
149
- 10819
+ 10645
149
227
- 11289
+ 11108
228
350
- 11289
+ 10645
- 358
- 628
- 10819
+ 351
+ 604
+ 10645
- 671
- 1926
- 10819
+ 630
+ 1479
+ 10645
- 2168
+ 1925
2380
- 1881
+ 2314
@@ -4599,67 +4548,67 @@
1
2
- 16464
+ 15737
2
9
- 10819
+ 10645
9
16
- 11760
+ 11571
16
25
- 11289
+ 11108
25
40
- 10819
+ 10645
40
57
- 10819
+ 10645
58
72
- 10819
+ 10645
73
103
- 11289
+ 11108
106
141
- 11760
+ 11571
148
226
- 11289
+ 11108
226
373
- 10819
+ 10645
381
1456
- 10819
+ 10645
1464
- 1613
- 1411
+ 1614
+ 1388
@@ -4675,67 +4624,67 @@
1
2
- 16464
+ 15737
2
4
- 8937
+ 8794
4
5
- 7526
+ 7405
5
6
- 7526
+ 7405
6
8
- 11760
+ 11571
8
13
- 12230
+ 12034
13
17
- 10819
+ 10645
17
25
- 11289
+ 11108
25
31
- 11760
+ 11571
31
38
- 10819
+ 10645
38
52
- 10819
+ 10645
52
64
- 10819
+ 10645
65
77
- 9408
+ 9257
@@ -4751,67 +4700,67 @@
1
2
- 16464
+ 15737
2
9
- 10819
+ 10645
9
16
- 11760
+ 11571
16
25
- 11289
+ 11108
25
40
- 10819
+ 10645
40
57
- 10819
+ 10645
58
71
- 10819
+ 10645
72
98
- 10819
+ 10645
101
140
- 11760
+ 11571
140
224
- 10819
+ 10645
224
360
- 10819
+ 10645
364
1185
- 10819
+ 10645
1254
- 1610
- 2352
+ 1611
+ 2314
@@ -4827,62 +4776,62 @@
1
2
- 16464
+ 15737
2
10
- 11289
+ 11108
10
14
- 10819
+ 10645
14
21
- 11289
+ 11108
22
31
- 11289
+ 11108
31
39
- 12701
+ 12497
39
48
- 12230
+ 12034
48
56
- 11760
+ 11571
56
64
- 11760
+ 11571
64
72
- 10819
+ 10645
72
77
- 11289
+ 11108
77
90
- 8467
+ 8331
@@ -4898,52 +4847,52 @@
1
2
- 581905
+ 572087
2
3
- 318001
+ 312426
3
4
- 199456
+ 196250
4
6
- 160882
+ 159221
6
10
- 190048
+ 186993
10
16
- 166057
+ 163387
16
25
- 168879
+ 166164
25
46
- 163704
+ 161073
46
169
- 159000
+ 156444
170
- 299
- 7056
+ 298
+ 6942
@@ -4959,42 +4908,42 @@
1
2
- 869799
+ 855354
2
3
- 280838
+ 275860
3
5
- 191459
+ 189307
5
8
- 181110
+ 178198
8
12
- 162293
+ 159684
12
18
- 166527
+ 163850
18
39
- 159941
+ 157370
39
- 299
- 103021
+ 298
+ 101365
@@ -5010,47 +4959,47 @@
1
2
- 612482
+ 601710
2
3
- 313297
+ 308260
3
4
- 202749
+ 199952
4
6
- 184403
+ 181901
6
9
- 180639
+ 177735
9
13
- 166997
+ 164313
13
19
- 173583
+ 170793
19
29
- 167468
+ 164776
29
52
- 113370
+ 111547
@@ -5066,22 +5015,22 @@
1
2
- 1545788
+ 1520938
2
3
- 351401
+ 345751
3
5
- 164645
+ 161998
5
16
- 53157
+ 52302
@@ -5097,52 +5046,52 @@
1
2
- 586609
+ 576716
2
3
- 318942
+ 313352
3
4
- 201808
+ 198564
4
6
- 167468
+ 165701
6
9
- 160412
+ 157833
9
14
- 178287
+ 175421
14
21
- 176406
+ 173570
21
32
- 163234
+ 160610
32
61
- 159000
+ 156444
61
66
- 2822
+ 2777
@@ -5158,72 +5107,72 @@
1
31
- 2822
+ 2777
42
85
- 2822
+ 2777
86
128
- 2822
+ 2777
129
229
- 2822
+ 2777
248
292
- 2822
+ 2777
- 292
+ 293
360
- 2822
+ 2777
376
459
- 2822
+ 2777
- 476
- 558
- 2822
+ 475
+ 559
+ 2777
565
- 620
- 2822
+ 623
+ 2777
626
699
- 2822
+ 2777
699
796
- 2822
+ 2777
819
- 1546
- 2822
+ 1548
+ 2777
1705
- 5645
- 2822
+ 5646
+ 2777
15306
15307
- 470
+ 462
@@ -5239,67 +5188,67 @@
1
18
- 2822
+ 2777
23
35
- 3292
+ 3239
38
43
- 2822
+ 2777
44
61
- 2822
+ 2777
65
73
- 2822
+ 2777
73
- 83
- 2822
+ 84
+ 3239
- 83
- 95
- 2822
+ 84
+ 97
+ 3239
- 96
+ 98
101
- 3292
+ 2314
101
105
- 3292
+ 3239
106
111
- 2822
+ 2777
111
123
- 2822
+ 2777
127
153
- 2822
+ 2777
169
- 299
- 1881
+ 298
+ 1851
@@ -5315,72 +5264,72 @@
1
19
- 2822
+ 2777
30
72
- 2822
+ 2777
83
122
- 2822
+ 2777
122
205
- 2822
+ 2777
214
261
- 2822
+ 2777
264
322
- 2822
+ 2777
325
380
- 2822
+ 2777
403
436
- 2822
+ 2777
454
474
- 2822
+ 2777
- 477
+ 478
514
- 2822
+ 2777
517
586
- 2822
+ 2777
587
- 831
- 2822
+ 832
+ 2777
1116
2197
- 2822
+ 2777
2387
2388
- 470
+ 462
@@ -5396,72 +5345,72 @@
1
19
- 2822
+ 2777
30
72
- 2822
+ 2777
83
122
- 2822
+ 2777
122
205
- 2822
+ 2777
214
261
- 2822
+ 2777
264
322
- 2822
+ 2777
325
380
- 2822
+ 2777
403
435
- 2822
+ 2777
454
474
- 2822
+ 2777
- 476
+ 477
513
- 2822
+ 2777
520
585
- 2822
+ 2777
587
831
- 2822
+ 2777
1121
2205
- 2822
+ 2777
2383
2384
- 470
+ 462
@@ -5477,67 +5426,67 @@
1
7
- 2822
+ 2777
7
11
- 3292
+ 3239
11
16
- 3292
+ 3239
16
22
- 2822
+ 2777
22
24
- 2352
+ 2314
24
27
- 3292
+ 3239
28
33
- 2822
+ 2777
33
40
- 3292
+ 3239
40
43
- 2822
+ 2777
43
49
- 2822
+ 2777
49
54
- 2822
+ 2777
54
74
- 2822
+ 2777
75
86
- 1881
+ 1851
@@ -5553,52 +5502,52 @@
1
2
- 589902
+ 579956
2
3
- 312356
+ 306872
3
4
- 198986
+ 196250
4
6
- 161352
+ 159221
6
10
- 189577
+ 186530
10
16
- 163704
+ 161073
16
25
- 171231
+ 168016
25
45
- 159471
+ 157370
45
160
- 159941
+ 157370
160
- 299
- 11289
+ 298
+ 11108
@@ -5614,47 +5563,47 @@
1
2
- 881560
+ 866925
2
3
- 270019
+ 265215
3
4
- 123249
+ 122193
4
6
- 142065
+ 139781
6
10
- 195693
+ 192547
10
15
- 168409
+ 165238
15
26
- 165586
+ 163387
26
120
- 159471
+ 156907
121
- 299
- 11760
+ 298
+ 11571
@@ -5670,22 +5619,22 @@
1
2
- 1538732
+ 1513995
2
3
- 349048
+ 343437
3
5
- 172172
+ 169404
5
10
- 57861
+ 56931
@@ -5701,47 +5650,47 @@
1
2
- 621890
+ 610967
2
3
- 304359
+ 299466
3
4
- 204631
+ 201804
4
6
- 186755
+ 184215
6
9
- 177817
+ 174958
9
13
- 169349
+ 166627
13
19
- 174524
+ 171718
19
29
- 162764
+ 160147
29
52
- 115722
+ 113862
@@ -5757,52 +5706,52 @@
1
2
- 596488
+ 586436
2
3
- 311415
+ 305946
3
4
- 198986
+ 196250
4
6
- 171701
+ 169404
6
9
- 157589
+ 155056
9
14
- 173583
+ 170793
14
21
- 180169
+ 177273
21
32
- 165116
+ 162461
32
60
- 159000
+ 156444
60
65
- 3763
+ 3702
@@ -5818,67 +5767,67 @@
1
2
- 5174
+ 5091
2
8
- 3763
+ 3702
9
186
- 3763
+ 3702
196
295
- 3763
+ 3702
- 298
+ 297
498
- 3763
+ 3702
503
- 553
- 3763
+ 554
+ 3702
563
634
- 3763
+ 3702
- 639
+ 640
762
- 3763
+ 3702
- 763
+ 765
871
- 3763
+ 3702
879
- 1081
- 3763
+ 1082
+ 3702
1083
- 1286
- 3763
+ 1283
+ 3702
- 1309
- 1590
- 3763
+ 1310
+ 1591
+ 3702
1682
2419
- 1881
+ 1851
@@ -5894,67 +5843,67 @@
1
2
- 5644
+ 5554
2
6
- 3763
+ 3702
6
65
- 3763
+ 3702
70
100
- 3763
+ 3702
100
111
- 3763
+ 3702
112
122
- 3763
+ 3702
122
134
- 3763
+ 3702
139
152
- 3763
+ 3702
152
160
- 4233
+ 4165
160
172
- 3763
+ 3702
172
176
- 3763
+ 3702
176
208
- 3763
+ 3702
240
- 299
- 940
+ 298
+ 925
@@ -5970,67 +5919,67 @@
1
2
- 5644
+ 5554
2
8
- 3763
+ 3702
9
106
- 3763
+ 3702
155
241
- 3763
+ 3702
253
335
- 3763
+ 3702
340
426
- 3763
+ 3702
437
488
- 3763
+ 3702
- 488
+ 489
574
- 3763
+ 3702
575
- 628
- 3763
+ 627
+ 3702
630
698
- 4233
+ 4165
701
817
- 3763
+ 3702
- 843
+ 841
1107
- 3763
+ 3702
- 1160
+ 1163
1174
- 940
+ 925
@@ -6046,67 +5995,67 @@
1
2
- 6115
+ 6017
2
4
- 3763
+ 3702
4
8
- 4233
+ 4165
8
15
- 3763
+ 3702
15
23
- 3763
+ 3702
23
29
- 3763
+ 3702
29
35
- 4233
+ 4165
35
39
- 3292
+ 3239
39
42
- 3763
+ 3702
42
44
- 2822
+ 2777
44
46
- 3763
+ 3702
46
49
- 3763
+ 3702
49
53
- 1411
+ 1388
@@ -6122,67 +6071,67 @@
1
2
- 5644
+ 5554
2
8
- 3763
+ 3702
9
156
- 3763
+ 3702
159
240
- 3763
+ 3702
251
334
- 3763
+ 3702
342
430
- 3763
+ 3702
435
490
- 4233
+ 3702
- 504
- 576
- 3763
+ 490
+ 575
+ 3702
- 576
- 631
- 3763
+ 575
+ 626
+ 3702
- 635
- 702
- 3763
+ 630
+ 700
+ 3702
- 702
- 813
- 3763
+ 701
+ 811
+ 3702
- 838
- 1109
- 3763
+ 812
+ 992
+ 3702
- 1161
+ 1108
1181
- 940
+ 1388
@@ -6192,31 +6141,31 @@
locations_stmt
- 3805462
+ 3813503
id
- 3805462
+ 3813503
container
- 3076
+ 3082
startLine
- 199416
+ 199837
startColumn
- 1866
+ 1870
endLine
- 193694
+ 194103
endColumn
- 2358
+ 2363
@@ -6230,7 +6179,7 @@
1
2
- 3805462
+ 3813503
@@ -6246,7 +6195,7 @@
1
2
- 3805462
+ 3813503
@@ -6262,7 +6211,7 @@
1
2
- 3805462
+ 3813503
@@ -6278,7 +6227,7 @@
1
2
- 3805462
+ 3813503
@@ -6294,7 +6243,7 @@
1
2
- 3805462
+ 3813503
@@ -6401,7 +6350,7 @@
169
371
- 266
+ 267
393
@@ -6462,12 +6411,12 @@
1
3
- 225
+ 226
3
7
- 266
+ 267
7
@@ -6482,12 +6431,12 @@
11
13
- 225
+ 226
13
14
- 225
+ 226
14
@@ -6639,7 +6588,7 @@
56
63
- 266
+ 267
63
@@ -6654,7 +6603,7 @@
69
71
- 225
+ 226
71
@@ -6695,67 +6644,67 @@
1
2
- 21494
+ 21539
2
3
- 15259
+ 15291
3
4
- 12449
+ 12475
4
6
- 14418
+ 14448
6
8
- 12490
+ 12516
8
11
- 16674
+ 16709
11
16
- 17228
+ 17264
16
22
- 15320
+ 15353
22
29
- 16941
+ 16976
29
37
- 17330
+ 17367
37
45
- 15054
+ 15085
45
56
- 16141
+ 16175
56
73
- 8614
+ 8632
@@ -6771,67 +6720,67 @@
1
2
- 22253
+ 22300
2
3
- 15689
+ 15723
3
4
- 12654
+ 12681
4
6
- 14356
+ 14387
6
8
- 12695
+ 12722
8
11
- 17535
+ 17572
11
16
- 16325
+ 16360
16
22
- 16182
+ 16216
22
29
- 16920
+ 16956
29
36
- 15956
+ 15990
36
44
- 16284
+ 16319
44
54
- 15607
+ 15640
54
69
- 6952
+ 6967
@@ -6847,57 +6796,57 @@
1
2
- 26765
+ 26821
2
3
- 20796
+ 20840
3
4
- 16776
+ 16812
4
5
- 16038
+ 16072
5
6
- 17392
+ 17429
6
7
- 19812
+ 19854
7
8
- 22704
+ 22752
8
9
- 20345
+ 20388
9
10
- 14972
+ 15003
10
12
- 16612
+ 16648
12
18
- 7198
+ 7214
@@ -6913,67 +6862,67 @@
1
2
- 34517
+ 34590
2
3
- 25739
+ 25794
3
4
- 18397
+ 18436
4
5
- 16182
+ 16216
5
6
- 12757
+ 12784
6
7
- 11998
+ 12023
7
8
- 10152
+ 10173
8
9
- 10952
+ 10975
9
10
- 10706
+ 10728
10
11
- 10500
+ 10523
11
12
- 10152
+ 10173
12
14
- 15751
+ 15784
14
24
- 11608
+ 11633
@@ -6989,67 +6938,67 @@
1
2
- 22089
+ 22135
2
3
- 16161
+ 16195
3
4
- 12921
+ 12948
4
6
- 16038
+ 16072
6
8
- 14664
+ 14695
8
10
- 13167
+ 13195
10
14
- 18253
+ 18292
14
18
- 16982
+ 17017
18
22
- 17535
+ 17572
22
26
- 18458
+ 18497
26
30
- 16346
+ 16380
30
36
- 15197
+ 15229
36
42
- 1599
+ 1603
@@ -7065,7 +7014,7 @@
1
2
- 225
+ 226
2
@@ -7212,7 +7161,7 @@
1
2
- 225
+ 226
2
@@ -7288,7 +7237,7 @@
1
2
- 225
+ 226
2
@@ -7435,67 +7384,67 @@
1
2
- 17371
+ 17408
2
3
- 14377
+ 14407
3
4
- 11464
+ 11489
4
6
- 15566
+ 15599
6
8
- 12469
+ 12496
8
11
- 15423
+ 15455
11
15
- 14602
+ 14633
15
21
- 16059
+ 16093
21
27
- 15382
+ 15414
27
34
- 14910
+ 14942
34
42
- 15710
+ 15743
42
52
- 15977
+ 16010
52
130
- 14377
+ 14407
@@ -7511,62 +7460,62 @@
1
2
- 24898
+ 24951
2
3
- 16100
+ 16134
3
4
- 12736
+ 12763
4
6
- 15628
+ 15661
6
8
- 14972
+ 15003
8
11
- 15854
+ 15887
11
16
- 17412
+ 17449
16
20
- 14561
+ 14592
20
26
- 17125
+ 17161
26
32
- 16223
+ 16257
32
39
- 14828
+ 14859
39
59
- 13351
+ 13380
@@ -7582,62 +7531,62 @@
1
2
- 32405
+ 32473
2
3
- 23709
+ 23759
3
4
- 18417
+ 18456
4
5
- 15115
+ 15147
5
6
- 13844
+ 13873
6
7
- 11649
+ 11674
7
8
- 11711
+ 11735
8
9
- 10890
+ 10913
9
10
- 10152
+ 10173
10
12
- 17925
+ 17963
12
15
- 17679
+ 17716
15
100
- 10193
+ 10214
@@ -7653,57 +7602,57 @@
1
2
- 24898
+ 24951
2
3
- 20345
+ 20388
3
4
- 16797
+ 16832
4
5
- 17761
+ 17798
5
6
- 18540
+ 18579
6
7
- 20386
+ 20429
7
8
- 22376
+ 22423
8
9
- 18704
+ 18744
9
10
- 12900
+ 12927
10
12
- 14992
+ 15024
12
18
- 5988
+ 6001
@@ -7719,67 +7668,67 @@
1
2
- 24652
+ 24704
2
3
- 16592
+ 16627
3
4
- 12510
+ 12537
4
6
- 17781
+ 17819
6
8
- 15300
+ 15332
8
10
- 12798
+ 12825
10
13
- 14377
+ 14407
13
16
- 14992
+ 15024
16
19
- 14623
+ 14654
19
22
- 14008
+ 14037
22
26
- 17084
+ 17120
26
31
- 15300
+ 15332
31
39
- 3671
+ 3679
@@ -8174,31 +8123,31 @@
locations_expr
- 13128112
+ 13165921
id
- 13128112
+ 13165921
container
- 4348
+ 4644
startLine
- 191499
+ 191904
startColumn
- 2461
+ 2466
endLine
- 191479
+ 191883
endColumn
- 2789
+ 2795
@@ -8212,7 +8161,7 @@
1
2
- 13128112
+ 13165921
@@ -8228,7 +8177,7 @@
1
2
- 13128112
+ 13165921
@@ -8244,7 +8193,7 @@
1
2
- 13128112
+ 13165921
@@ -8260,7 +8209,7 @@
1
2
- 13128112
+ 13165921
@@ -8276,7 +8225,7 @@
1
2
- 13128112
+ 13165921
@@ -8292,72 +8241,72 @@
1
2
- 369
+ 411
2
6
- 266
+ 328
6
- 10
- 348
+ 11
+ 369
- 10
- 34
- 328
+ 12
+ 26
+ 369
- 46
- 136
- 328
+ 27
+ 96
+ 349
- 166
- 594
- 328
+ 100
+ 514
+ 349
- 677
- 1614
- 328
+ 525
+ 1401
+ 349
- 1623
- 2416
- 328
+ 1526
+ 2343
+ 349
- 2490
- 3669
- 328
+ 2404
+ 3615
+ 349
- 3705
- 5161
- 328
+ 3668
+ 5162
+ 349
5341
- 6838
- 328
+ 7345
+ 349
- 7344
- 9012
- 328
+ 7399
+ 9307
+ 349
- 9073
- 13259
- 328
+ 9382
+ 16759
+ 349
- 13617
+ 18811
18812
- 82
+ 20
@@ -8373,67 +8322,67 @@
1
2
- 451
+ 493
2
- 5
- 348
+ 4
+ 369
- 5
+ 4
10
- 328
+ 369
10
- 22
- 328
+ 20
+ 349
- 24
- 87
- 328
+ 20
+ 51
+ 349
- 97
- 207
- 328
+ 65
+ 151
+ 349
- 213
- 437
- 328
+ 161
+ 360
+ 349
- 454
- 689
- 328
+ 361
+ 577
+ 349
- 719
- 977
- 328
+ 590
+ 923
+ 349
- 997
- 1292
- 328
+ 928
+ 1265
+ 349
- 1332
- 1781
- 328
+ 1268
+ 1742
+ 349
- 1851
+ 1781
2320
- 328
+ 349
2491
4241
- 266
+ 267
@@ -8449,47 +8398,52 @@
1
2
- 451
+ 493
2
- 5
- 389
+ 4
+ 349
- 5
- 10
+ 4
+ 7
+ 390
+
+
+ 7
+ 16
+ 349
+
+
+ 16
+ 37
+ 349
+
+
+ 37
+ 59
+ 390
+
+
+ 59
+ 66
369
- 10
- 27
- 328
+ 66
+ 68
+ 267
- 27
- 55
- 369
-
-
- 55
- 64
- 348
-
-
- 64
- 67
- 246
-
-
- 67
+ 68
69
- 369
+ 205
69
70
- 307
+ 308
70
@@ -8499,22 +8453,22 @@
71
72
- 307
+ 308
72
74
- 266
+ 267
74
- 83
- 328
+ 92
+ 369
- 90
- 108
- 82
+ 94
+ 109
+ 41
@@ -8530,67 +8484,67 @@
1
2
- 451
+ 493
2
- 5
- 348
+ 4
+ 369
- 5
+ 4
10
- 328
+ 369
10
- 22
- 328
+ 20
+ 349
- 24
- 87
- 328
+ 20
+ 51
+ 349
- 97
- 207
- 328
+ 65
+ 151
+ 349
- 214
- 437
- 328
+ 162
+ 360
+ 349
- 454
- 691
- 328
+ 361
+ 578
+ 349
- 719
- 978
- 328
+ 591
+ 926
+ 349
- 998
- 1293
- 328
+ 930
+ 1266
+ 349
- 1332
- 1785
- 328
+ 1272
+ 1742
+ 349
- 1855
+ 1785
2324
- 328
+ 349
2500
4416
- 266
+ 267
@@ -8606,37 +8560,42 @@
1
2
- 410
+ 452
2
- 5
- 389
-
-
- 5
- 9
+ 4
328
- 9
- 29
- 328
+ 4
+ 7
+ 369
- 30
- 55
- 328
+ 7
+ 15
+ 349
- 55
- 69
- 389
+ 15
+ 36
+ 349
- 69
+ 36
+ 61
+ 349
+
+
+ 61
+ 70
+ 349
+
+
+ 70
73
- 348
+ 267
73
@@ -8651,22 +8610,22 @@
76
77
- 410
+ 411
77
79
- 348
+ 349
79
- 83
- 328
+ 84
+ 349
- 83
+ 84
116
- 287
+ 267
@@ -8682,67 +8641,67 @@
1
5
- 16079
+ 16113
5
9
- 16448
+ 16483
9
15
- 15997
+ 16031
15
23
- 15074
+ 15106
23
32
- 15115
+ 15147
32
44
- 14972
+ 15003
44
60
- 14726
+ 14757
60
80
- 14808
+ 14818
80
103
- 14582
+ 14633
103
130
- 14787
+ 14777
130
159
- 14561
+ 14531
159
194
- 14561
+ 14613
194
302
- 9783
+ 9886
@@ -8758,62 +8717,62 @@
1
2
- 23463
+ 23512
2
3
- 15587
+ 15620
3
4
- 11321
+ 11345
4
6
- 16325
+ 16360
6
8
- 13597
+ 13626
8
11
- 16407
+ 16442
11
16
- 17330
+ 17346
16
21
- 16407
+ 16442
21
28
- 16612
+ 16648
28
35
- 15956
+ 15805
35
43
- 15936
+ 15846
43
60
- 12551
+ 12907
@@ -8829,62 +8788,62 @@
1
4
- 15936
+ 15969
4
7
- 17494
+ 17531
7
11
- 16653
+ 16689
11
16
- 17371
+ 17408
16
21
- 17474
+ 17511
21
26
- 15033
+ 15065
26
31
- 16141
+ 16175
31
36
- 17699
+ 17716
36
40
- 15792
+ 15702
40
44
- 16530
+ 16298
44
49
- 16592
+ 16894
49
63
- 8778
+ 8940
@@ -8900,27 +8859,27 @@
1
2
- 101769
+ 101943
2
3
- 44567
+ 44620
3
4
- 27503
+ 27643
4
6
- 14541
+ 14572
6
23
- 3117
+ 3124
@@ -8936,67 +8895,67 @@
1
4
- 16920
+ 16956
4
7
- 16612
+ 16648
7
11
- 16387
+ 16421
11
16
- 16182
+ 16216
16
21
- 16407
+ 16442
21
27
- 16735
+ 16771
27
33
- 16407
+ 16442
33
38
- 14459
+ 14469
38
43
- 15505
+ 15538
43
47
- 14746
+ 14695
47
52
- 16715
+ 16771
52
65
- 14377
+ 14448
- 66
+ 65
70
- 41
+ 82
@@ -9012,7 +8971,7 @@
1
2
- 307
+ 308
2
@@ -9031,41 +8990,41 @@
43
- 251
+ 253
184
- 279
- 840
+ 280
+ 849
184
- 951
- 1889
+ 956
+ 1895
184
- 2097
- 4180
+ 2100
+ 4183
184
- 4240
- 7018
+ 4242
+ 7021
184
- 7166
- 11389
+ 7174
+ 11394
184
- 12326
- 15119
+ 12337
+ 15120
184
- 15349
+ 15374
30165
184
@@ -9107,48 +9066,53 @@
7
- 28
+ 32
184
- 41
- 98
+ 43
+ 99
184
- 103
- 122
+ 104
+ 123
184
- 123
- 130
- 205
-
-
- 132
- 138
+ 124
+ 133
184
- 138
+ 133
+ 139
+ 164
+
+
+ 139
142
- 205
+ 164
142
144
- 184
+ 143
144
- 148
- 184
+ 147
+ 226
- 149
+ 148
155
- 164
+ 205
+
+
+ 155
+ 158
+ 41
@@ -9164,7 +9128,7 @@
1
2
- 307
+ 308
2
@@ -9183,36 +9147,36 @@
20
- 151
+ 152
184
- 196
- 585
+ 199
+ 589
184
- 622
- 1287
+ 633
+ 1290
184
- 1368
- 2342
+ 1370
+ 2344
184
- 2570
+ 2574
3505
184
- 3522
+ 3527
4711
184
- 4732
+ 4734
5298
184
@@ -9240,7 +9204,7 @@
1
2
- 307
+ 308
2
@@ -9259,36 +9223,36 @@
20
- 151
+ 152
184
- 196
- 585
+ 199
+ 589
184
- 647
- 1289
+ 651
+ 1292
184
- 1368
- 2346
+ 1370
+ 2348
184
- 2573
+ 2575
3511
184
- 3528
+ 3533
4712
184
- 4735
+ 4737
5324
184
@@ -9370,13 +9334,13 @@
74
- 83
- 184
+ 84
+ 226
- 83
+ 84
96
- 143
+ 102
@@ -9392,67 +9356,67 @@
1
5
- 16100
+ 16134
5
9
- 16448
+ 16483
9
15
- 15772
+ 15805
15
23
- 15054
+ 15085
23
32
- 15607
+ 15640
32
44
- 14705
+ 14736
44
60
- 14459
+ 14489
60
80
- 15238
+ 15250
80
103
- 14479
+ 14531
103
130
- 14787
+ 14757
130
- 159
- 14459
+ 160
+ 14880
- 159
- 193
- 14377
+ 160
+ 195
+ 14551
- 193
+ 195
299
- 9988
+ 9536
@@ -9468,67 +9432,67 @@
1
2
- 23463
+ 23512
2
3
- 15525
+ 15558
3
4
- 11321
+ 11345
4
6
- 16018
+ 16051
6
8
- 13454
+ 13482
8
11
- 16469
+ 16504
11
15
- 14459
+ 14428
15
20
- 16674
+ 16771
20
26
- 14972
+ 14983
26
33
- 16120
+ 16051
33
40
- 14726
+ 14633
40
49
- 14726
+ 14592
49
60
- 3548
+ 3966
@@ -9544,27 +9508,27 @@
1
2
- 95349
+ 95469
2
3
- 49838
+ 50005
3
4
- 29308
+ 29370
4
6
- 15566
+ 15599
6
11
- 1415
+ 1438
@@ -9580,62 +9544,62 @@
1
4
- 15792
+ 15825
4
7
- 17412
+ 17449
7
11
- 16448
+ 16483
11
16
- 17310
+ 17346
16
21
- 17269
+ 17305
21
26
- 15115
+ 15147
26
31
- 16264
+ 16298
31
36
- 17679
+ 17675
36
40
- 15341
+ 15291
40
44
- 16592
+ 16442
44
49
- 16735
+ 16976
49
63
- 9516
+ 9639
@@ -9651,67 +9615,62 @@
1
4
- 17146
+ 17182
4
7
- 16756
+ 16791
7
11
- 16387
+ 16421
11
16
- 16838
+ 16874
16
21
- 15977
+ 16010
21
26
- 14479
+ 14510
26
32
- 16120
+ 16154
32
- 37
- 14377
+ 38
+ 17490
- 37
- 42
- 15833
+ 38
+ 43
+ 16134
- 42
- 46
- 14233
+ 43
+ 47
+ 14469
- 46
- 51
- 17248
+ 47
+ 52
+ 16565
- 51
- 59
- 14459
-
-
- 59
+ 52
69
- 1620
+ 13277
@@ -9727,12 +9686,12 @@
1
2
- 225
+ 226
2
4
- 225
+ 226
4
@@ -9747,45 +9706,45 @@
16
51
- 225
+ 226
56
- 615
- 225
+ 617
+ 226
- 834
- 2288
- 225
+ 835
+ 2297
+ 226
2328
- 4150
- 225
+ 4152
+ 226
4177
- 7135
- 225
+ 7139
+ 226
- 8237
- 11757
- 225
+ 8241
+ 11758
+ 226
- 12358
+ 12367
15463
- 225
+ 226
- 15685
- 18241
- 225
+ 15690
+ 18245
+ 226
- 18731
+ 18733
19130
82
@@ -9818,52 +9777,52 @@
6
12
- 225
+ 226
12
41
- 225
+ 226
50
- 113
- 225
+ 114
+ 226
- 113
+ 115
128
- 225
+ 226
128
- 135
+ 137
205
- 135
- 139
+ 137
+ 142
246
- 139
- 146
- 205
+ 142
+ 147
+ 143
- 146
+ 147
148
- 205
+ 123
148
- 152
+ 151
246
- 152
- 153
- 41
+ 151
+ 163
+ 184
@@ -9879,7 +9838,7 @@
1
2
- 307
+ 308
2
@@ -9894,47 +9853,47 @@
8
15
- 225
+ 226
18
- 53
- 225
+ 54
+ 226
74
491
- 225
+ 226
- 512
- 1332
- 225
+ 514
+ 1335
+ 226
- 1392
- 2419
- 225
+ 1397
+ 2422
+ 226
- 2763
+ 2764
3740
- 225
+ 226
3801
- 4530
- 225
+ 4533
+ 226
- 4641
- 5303
- 225
+ 4642
+ 5304
+ 226
5377
5735
- 225
+ 226
5747
@@ -9955,7 +9914,7 @@
1
2
- 266
+ 267
2
@@ -9974,43 +9933,43 @@
14
- 21
- 225
+ 22
+ 246
- 21
+ 23
28
- 246
+ 226
28
36
- 246
+ 226
36
- 42
- 246
+ 41
+ 226
- 42
- 49
- 246
+ 41
+ 47
+ 226
- 49
- 59
- 225
+ 47
+ 56
+ 226
- 59
- 65
- 184
+ 56
+ 64
+ 226
- 66
- 71
- 205
+ 64
+ 72
+ 226
@@ -10026,7 +9985,7 @@
1
2
- 307
+ 308
2
@@ -10041,47 +10000,47 @@
8
15
- 225
+ 226
17
- 53
- 225
+ 54
+ 226
74
473
- 225
+ 226
- 500
- 1302
- 225
+ 502
+ 1306
+ 226
- 1356
+ 1361
2389
- 225
+ 226
- 2626
+ 2627
3666
- 225
+ 226
3731
- 4489
- 225
+ 4491
+ 226
- 4638
- 5281
- 225
+ 4639
+ 5282
+ 226
- 5366
+ 5367
5729
- 225
+ 226
5734
@@ -10096,23 +10055,23 @@
numlines
- 1406545
+ 1383933
element_id
- 1399488
+ 1376990
num_lines
- 102550
+ 100902
num_code
- 85615
+ 84239
num_comment
- 60213
+ 59245
@@ -10126,12 +10085,12 @@
1
2
- 1392432
+ 1370047
2
3
- 7056
+ 6942
@@ -10147,12 +10106,12 @@
1
2
- 1393373
+ 1370973
2
3
- 6115
+ 6017
@@ -10168,7 +10127,7 @@
1
2
- 1399488
+ 1376990
@@ -10184,27 +10143,27 @@
1
2
- 68680
+ 67576
2
3
- 12230
+ 12034
3
4
- 7526
+ 7405
4
21
- 7997
+ 7868
29
926
- 6115
+ 6017
@@ -10220,27 +10179,27 @@
1
2
- 71032
+ 69890
2
3
- 12230
+ 12034
3
4
- 8467
+ 8331
4
6
- 9408
+ 9257
6
7
- 1411
+ 1388
@@ -10256,22 +10215,22 @@
1
2
- 70092
+ 68965
2
3
- 15053
+ 14811
3
4
- 10819
+ 10645
4
7
- 6585
+ 6479
@@ -10287,27 +10246,27 @@
1
2
- 53157
+ 52302
2
3
- 14582
+ 14348
3
5
- 6585
+ 6479
5
42
- 6585
+ 6479
44
927
- 4704
+ 4628
@@ -10323,27 +10282,27 @@
1
2
- 53157
+ 52302
2
3
- 16934
+ 16662
3
5
- 6115
+ 6017
5
8
- 6585
+ 6479
8
12
- 2822
+ 2777
@@ -10359,27 +10318,27 @@
1
2
- 53627
+ 52765
2
3
- 15994
+ 15737
3
5
- 7526
+ 7405
5
7
- 5174
+ 5091
7
10
- 3292
+ 3239
@@ -10395,32 +10354,32 @@
1
2
- 34810
+ 34251
2
3
- 9408
+ 9257
3
4
- 4233
+ 4165
4
6
- 4704
+ 4628
6
11
- 5174
+ 5091
17
2622
- 1881
+ 1851
@@ -10436,32 +10395,32 @@
1
2
- 34810
+ 34251
2
3
- 9408
+ 9257
3
4
- 4233
+ 4165
4
6
- 4704
+ 4628
6
8
- 4704
+ 4628
10
38
- 2352
+ 2314
@@ -10477,32 +10436,32 @@
1
2
- 34810
+ 34251
2
3
- 9408
+ 9257
3
4
- 4233
+ 4165
4
6
- 4704
+ 4628
6
10
- 4704
+ 4628
10
37
- 2352
+ 2314
@@ -10512,31 +10471,31 @@
diagnostics
- 582773
+ 72092
id
- 582773
+ 72092
severity
- 209
+ 23
error_tag
- 7851
+ 80
error_message
- 56215
+ 126
full_error_message
- 582040
+ 62547
location
- 369218
+ 149
@@ -10550,7 +10509,7 @@
1
2
- 582773
+ 72092
@@ -10566,7 +10525,7 @@
1
2
- 582773
+ 72092
@@ -10582,7 +10541,7 @@
1
2
- 582773
+ 72092
@@ -10598,7 +10557,7 @@
1
2
- 582773
+ 72092
@@ -10614,7 +10573,7 @@
1
2
- 582773
+ 72092
@@ -10628,14 +10587,14 @@
12
- 254
- 255
- 104
+ 4
+ 5
+ 11
- 5313
- 5314
- 104
+ 6250
+ 6251
+ 11
@@ -10649,14 +10608,14 @@
12
- 5
- 6
- 104
+ 1
+ 2
+ 11
- 70
- 71
- 104
+ 6
+ 7
+ 11
@@ -10669,508 +10628,388 @@
12
-
- 7
- 8
- 104
-
-
- 530
- 531
- 104
-
-
-
-
-
-
- severity
- full_error_message
-
-
- 12
-
-
- 254
- 255
- 104
-
-
- 5306
- 5307
- 104
-
-
-
-
-
-
- severity
- location
-
-
- 12
-
-
- 174
- 175
- 104
-
-
- 3429
- 3430
- 104
-
-
-
-
-
-
- error_tag
- id
-
-
- 12
-
-
- 1
- 2
- 1046
-
-
- 2
- 3
- 942
-
3
4
- 523
-
-
- 4
- 5
- 523
-
-
- 5
- 7
- 628
-
-
- 7
- 11
- 628
-
-
- 11
- 16
- 628
-
-
- 16
- 21
- 628
-
-
- 21
- 32
- 628
-
-
- 44
- 72
- 628
-
-
- 112
- 540
- 628
-
-
- 595
- 1254
- 418
-
-
-
-
-
-
- error_tag
- severity
-
-
- 12
-
-
- 1
- 2
- 7851
-
-
-
-
-
-
- error_tag
- error_message
-
-
- 12
-
-
- 1
- 2
- 5443
-
-
- 2
- 3
- 418
-
-
- 3
- 4
- 523
-
-
- 4
- 6
- 628
-
-
- 7
- 35
- 628
-
-
- 59
- 294
- 209
-
-
-
-
-
-
- error_tag
- full_error_message
-
-
- 12
-
-
- 1
- 2
- 1151
-
-
- 2
- 3
- 942
-
-
- 3
- 4
- 523
-
-
- 4
- 5
- 523
-
-
- 5
- 7
- 628
-
-
- 7
- 12
- 628
-
-
- 13
- 16
- 523
-
-
- 16
- 21
- 628
-
-
- 21
- 32
- 628
-
-
- 44
- 72
- 628
-
-
- 112
- 540
- 628
-
-
- 595
- 1254
- 418
-
-
-
-
-
-
- error_tag
- location
-
-
- 12
-
-
- 1
- 2
- 1674
-
-
- 2
- 3
- 942
-
-
- 3
- 4
- 418
-
-
- 4
- 5
- 523
-
-
- 5
- 6
- 523
-
-
- 6
- 7
- 523
-
-
- 7
- 15
- 628
-
-
- 15
- 20
- 628
-
-
- 23
- 44
- 628
-
-
- 44
- 95
- 628
-
-
- 139
- 630
- 628
-
-
- 764
- 765
- 104
-
-
-
-
-
-
- error_message
- id
-
-
- 12
-
-
- 1
- 2
- 24809
-
-
- 2
- 3
- 13608
-
-
- 3
- 4
- 4187
-
-
- 4
- 6
- 4606
-
-
- 6
- 14
- 4292
-
-
- 14
- 227
- 4292
-
-
- 405
- 1254
- 418
-
-
-
-
-
-
- error_message
- severity
-
-
- 12
-
-
- 1
- 2
- 56215
-
-
-
-
-
-
- error_message
- error_tag
-
-
- 12
-
-
- 1
- 2
- 56215
-
-
-
-
-
-
- error_message
- full_error_message
-
-
- 12
-
-
- 1
- 2
- 24914
-
-
- 2
- 3
- 13608
-
-
- 3
- 4
- 4187
-
-
- 4
- 6
- 4606
-
-
- 6
- 15
- 4396
-
-
- 15
- 540
- 4292
-
-
- 595
- 1254
- 209
-
-
-
-
-
-
- error_message
- location
-
-
- 12
-
-
- 1
- 2
- 36011
-
-
- 2
- 3
- 6909
-
-
- 3
- 5
- 4710
-
-
- 5
- 12
- 4292
-
-
- 12
- 765
- 4292
-
-
-
-
-
-
- full_error_message
- id
-
-
- 12
-
-
- 1
- 2
- 581935
+ 11
8
9
- 104
+ 11
+
+
+
+
+
+
+ severity
+ full_error_message
+
+
+ 12
+
+
+ 4
+ 5
+ 11
+
+
+ 5422
+ 5423
+ 11
+
+
+
+
+
+
+ severity
+ location
+
+
+ 12
+
+
+ 4
+ 5
+ 11
+
+
+ 9
+ 10
+ 11
+
+
+
+
+
+
+ error_tag
+ id
+
+
+ 12
+
+
+ 1
+ 2
+ 11
+
+
+ 2
+ 3
+ 11
+
+
+ 4
+ 5
+ 11
+
+
+ 5
+ 6
+ 11
+
+
+ 417
+ 418
+ 11
+
+
+ 829
+ 830
+ 11
+
+
+ 4996
+ 4997
+ 11
+
+
+
+
+
+
+ error_tag
+ severity
+
+
+ 12
+
+
+ 1
+ 2
+ 80
+
+
+
+
+
+
+ error_tag
+ error_message
+
+
+ 12
+
+
+ 1
+ 2
+ 57
+
+
+ 3
+ 4
+ 23
+
+
+
+
+
+
+ error_tag
+ full_error_message
+
+
+ 12
+
+
+ 1
+ 2
+ 23
+
+
+ 2
+ 3
+ 11
+
+
+ 4
+ 5
+ 11
+
+
+ 5
+ 6
+ 11
+
+
+ 417
+ 418
+ 11
+
+
+ 4996
+ 4997
+ 11
+
+
+
+
+
+
+ error_tag
+ location
+
+
+ 12
+
+
+ 1
+ 2
+ 46
+
+
+ 2
+ 3
+ 11
+
+
+ 4
+ 5
+ 11
+
+
+ 5
+ 6
+ 11
+
+
+
+
+
+
+ error_message
+ id
+
+
+ 12
+
+
+ 1
+ 2
+ 34
+
+
+ 2
+ 3
+ 23
+
+
+ 5
+ 6
+ 11
+
+
+ 10
+ 11
+ 11
+
+
+ 75
+ 76
+ 11
+
+
+ 332
+ 333
+ 11
+
+
+ 829
+ 830
+ 11
+
+
+ 4996
+ 4997
+ 11
+
+
+
+
+
+
+ error_message
+ severity
+
+
+ 12
+
+
+ 1
+ 2
+ 126
+
+
+
+
+
+
+ error_message
+ error_tag
+
+
+ 12
+
+
+ 1
+ 2
+ 126
+
+
+
+
+
+
+ error_message
+ full_error_message
+
+
+ 12
+
+
+ 1
+ 2
+ 46
+
+
+ 2
+ 3
+ 23
+
+
+ 5
+ 6
+ 11
+
+
+ 10
+ 11
+ 11
+
+
+ 75
+ 76
+ 11
+
+
+ 332
+ 333
+ 11
+
+
+ 4996
+ 4997
+ 11
+
+
+
+
+
+
+ error_message
+ location
+
+
+ 12
+
+
+ 1
+ 2
+ 92
+
+
+ 2
+ 3
+ 23
+
+
+ 5
+ 6
+ 11
+
+
+
+
+
+
+ full_error_message
+ id
+
+
+ 12
+
+
+ 1
+ 2
+ 62536
+
+
+ 829
+ 830
+ 11
@@ -11186,7 +11025,7 @@
1
2
- 582040
+ 62547
@@ -11202,7 +11041,7 @@
1
2
- 582040
+ 62547
@@ -11218,7 +11057,7 @@
1
2
- 582040
+ 62547
@@ -11234,7 +11073,7 @@
1
2
- 582040
+ 62547
@@ -11250,17 +11089,12 @@
1
2
- 206959
+ 138
- 2
- 3
- 135774
-
-
- 3
- 13
- 26484
+ 6242
+ 6243
+ 11
@@ -11276,12 +11110,7 @@
1
2
- 361262
-
-
- 2
- 3
- 7955
+ 149
@@ -11297,12 +11126,12 @@
1
2
- 354458
+ 138
- 2
- 6
- 14760
+ 3
+ 4
+ 11
@@ -11318,12 +11147,12 @@
1
2
- 353830
+ 138
- 2
+ 5
6
- 15388
+ 11
@@ -11339,17 +11168,12 @@
1
2
- 207063
+ 138
- 2
- 3
- 135774
-
-
- 3
- 13
- 26380
+ 5414
+ 5415
+ 11
@@ -11359,15 +11183,15 @@
files
- 124189
+ 122193
id
- 124189
+ 122193
name
- 124189
+ 122193
@@ -11381,7 +11205,7 @@
1
2
- 124189
+ 122193
@@ -11397,7 +11221,7 @@
1
2
- 124189
+ 122193
@@ -11407,15 +11231,15 @@
folders
- 15994
+ 15274
id
- 15994
+ 15274
name
- 15994
+ 15274
@@ -11429,7 +11253,7 @@
1
2
- 15994
+ 15274
@@ -11445,7 +11269,7 @@
1
2
- 15994
+ 15274
@@ -11455,15 +11279,15 @@
containerparent
- 139243
+ 136541
parent
- 15994
+ 15274
child
- 139243
+ 136541
@@ -11477,32 +11301,32 @@
1
2
- 7056
+ 6479
2
3
- 3292
+ 3239
3
5
- 1411
+ 1388
5
12
- 1411
+ 1388
23
28
- 1411
+ 1388
40
67
- 1411
+ 1388
@@ -11518,7 +11342,7 @@
1
2
- 139243
+ 136541
@@ -11528,11 +11352,11 @@
fileannotations
- 5254886
+ 5237857
id
- 5019
+ 5002
kind
@@ -11540,11 +11364,11 @@
name
- 56112
+ 55930
value
- 47173
+ 47020
@@ -11558,12 +11382,12 @@
1
2
- 173
+ 172
2
3
- 4845
+ 4829
@@ -11579,42 +11403,42 @@
1
102
- 393
+ 391
102
225
- 381
+ 380
227
299
- 381
+ 380
301
452
- 404
+ 403
452
555
- 381
+ 380
559
626
- 381
+ 380
626
716
- 381
+ 380
729
904
- 381
+ 380
904
@@ -11624,12 +11448,12 @@
936
937
- 1457
+ 1452
1083
2036
- 381
+ 380
2293
@@ -11650,52 +11474,52 @@
1
114
- 393
+ 391
114
275
- 381
+ 380
275
363
- 381
+ 380
393
638
- 381
+ 380
643
744
- 381
+ 380
751
955
- 381
+ 380
955
1087
- 381
+ 380
1088
1501
- 254
+ 253
1501
1502
- 1457
+ 1452
1504
1874
- 381
+ 380
1972
@@ -11779,62 +11603,62 @@
1
2
- 9078
+ 9048
2
3
- 6372
+ 6351
3
5
- 4278
+ 4265
5
9
- 4371
+ 4357
9
14
- 4082
+ 4069
14
18
- 4278
+ 4265
18
20
- 4834
+ 4818
20
34
- 4325
+ 4311
34
128
- 4614
+ 4599
128
229
- 4221
+ 4207
229
387
- 4348
+ 4334
387
434
- 1306
+ 1302
@@ -11850,7 +11674,7 @@
1
2
- 56112
+ 55930
@@ -11866,62 +11690,62 @@
1
2
- 9089
+ 9060
2
3
- 8257
+ 8230
3
4
- 2625
+ 2616
4
6
- 4625
+ 4610
6
9
- 4232
+ 4219
9
14
- 4313
+ 4299
14
17
- 4232
+ 4219
17
22
- 4706
+ 4691
22
41
- 4313
+ 4299
41
82
- 4267
+ 4253
82
157
- 4209
+ 4195
158
1895
- 1237
+ 1233
@@ -11937,67 +11761,67 @@
1
2
- 7332
+ 7308
2
5
- 2289
+ 2282
5
8
- 3411
+ 3400
8
15
- 3619
+ 3608
15
17
- 2602
+ 2593
17
19
- 4244
+ 4230
19
34
- 3411
+ 3400
34
189
- 3712
+ 3700
189
201
- 3700
+ 3688
201
266
- 3642
+ 3631
266
321
- 3770
+ 3757
322
399
- 4047
+ 4034
399
435
- 1387
+ 1383
@@ -12013,7 +11837,7 @@
1
2
- 47161
+ 47008
2
@@ -12034,67 +11858,67 @@
1
2
- 7355
+ 7331
2
5
- 2648
+ 2639
5
8
- 3596
+ 3585
8
15
- 3642
+ 3631
15
17
- 2902
+ 2893
17
19
- 3677
+ 3665
19
29
- 3596
+ 3585
29
39
- 3758
+ 3746
39
48
- 3700
+ 3688
48
74
- 3654
+ 3642
74
102
- 3538
+ 3527
102
119
- 3689
+ 3677
119
146
- 1410
+ 1406
@@ -12104,15 +11928,15 @@
inmacroexpansion
- 109313317
+ 109604497
id
- 17941916
+ 17996941
inv
- 2682068
+ 2695871
@@ -12126,37 +11950,37 @@
1
3
- 1566018
+ 1578646
3
5
- 1071344
+ 1074226
5
6
- 1179814
+ 1182860
6
7
- 4800000
+ 4812393
7
8
- 6359380
+ 6375799
8
9
- 2595248
+ 2601948
9
150
- 370109
+ 371065
@@ -12172,57 +11996,57 @@
1
2
- 371855
+ 377822
2
3
- 540116
+ 543235
3
4
- 349917
+ 350956
4
7
- 199815
+ 200339
7
8
- 206290
+ 206822
8
9
- 240882
+ 241503
9
10
- 2201
+ 2206
10
11
- 324132
+ 324968
11
337
- 223911
+ 224493
339
- 422
- 201383
+ 423
+ 206025
- 422
+ 423
7616
- 21563
+ 17496
@@ -12232,15 +12056,15 @@
affectedbymacroexpansion
- 35540532
+ 35632294
id
- 5135277
+ 5148536
inv
- 2773181
+ 2780342
@@ -12254,37 +12078,37 @@
1
2
- 2804212
+ 2811452
2
3
- 557797
+ 559237
3
4
- 263804
+ 264485
4
5
- 563439
+ 564894
5
12
- 390272
+ 391280
12
50
- 405705
+ 406753
50
9900
- 150045
+ 150433
@@ -12300,67 +12124,67 @@
1
4
- 228162
+ 228751
4
7
- 230823
+ 231419
7
9
- 219560
+ 220127
9
12
- 250042
+ 250688
12
13
- 332588
+ 333446
13
14
- 164899
+ 165325
14
15
- 297600
+ 298368
15
16
- 121336
+ 121649
16
17
- 275457
+ 276168
17
18
- 146328
+ 146706
18
20
- 251085
+ 251734
20
25
- 208109
+ 208646
25
109
- 47186
+ 47308
@@ -12370,19 +12194,19 @@
macroinvocations
- 33894981
+ 33818017
id
- 33894981
+ 33818017
macro_id
- 81381
+ 81175
location
- 778557
+ 776461
kind
@@ -12400,7 +12224,7 @@
1
2
- 33894981
+ 33818017
@@ -12416,7 +12240,7 @@
1
2
- 33894981
+ 33818017
@@ -12432,7 +12256,7 @@
1
2
- 33894981
+ 33818017
@@ -12448,57 +12272,57 @@
1
2
- 17578
+ 17521
2
3
- 16977
+ 16922
3
4
- 3700
+ 3688
4
5
- 4880
+ 4853
5
8
- 6048
+ 6005
8
14
- 6406
+ 6432
14
29
- 6291
+ 6305
29
- 72
- 6106
+ 73
+ 6201
- 72
- 247
- 6129
+ 73
+ 257
+ 6097
- 248
- 4166
- 6106
+ 257
+ 5161
+ 6097
- 4220
+ 5432
168296
- 1156
+ 1048
@@ -12514,37 +12338,37 @@
1
2
- 43506
+ 43366
2
3
- 10651
+ 10616
3
4
- 5285
+ 5268
4
6
- 6985
+ 6997
6
13
- 6626
+ 6616
13
66
- 6140
+ 6132
66
3614
- 2185
+ 2178
@@ -12560,12 +12384,12 @@
1
2
- 75553
+ 75308
2
3
- 5828
+ 5867
@@ -12581,37 +12405,37 @@
1
2
- 320982
+ 320046
2
3
- 177751
+ 170824
3
4
- 47300
+ 50732
4
5
- 59605
+ 61717
5
9
- 68533
+ 68887
9
23
- 58425
+ 58340
23
244365
- 45958
+ 45913
@@ -12627,12 +12451,12 @@
1
2
- 731280
+ 729337
2
350
- 47277
+ 47123
@@ -12648,7 +12472,7 @@
1
2
- 778557
+ 776461
@@ -12662,13 +12486,13 @@
12
- 20414
- 20415
+ 20464
+ 20465
11
- 2910446
- 2910447
+ 2913248
+ 2913249
11
@@ -12683,13 +12507,13 @@
12
- 2123
- 2124
+ 2128
+ 2129
11
- 5418
- 5419
+ 5423
+ 5424
11
@@ -12704,13 +12528,13 @@
12
- 6291
- 6292
+ 6315
+ 6316
11
- 61030
- 61031
+ 61043
+ 61044
11
@@ -12721,15 +12545,15 @@
macroparent
- 30455592
+ 30368148
id
- 30455592
+ 30368148
parent_id
- 23698199
+ 23632653
@@ -12743,7 +12567,7 @@
1
2
- 30455592
+ 30368148
@@ -12759,17 +12583,17 @@
1
2
- 18307171
+ 18259095
2
3
- 4540062
+ 4525350
3
88
- 850965
+ 848207
@@ -12779,15 +12603,15 @@
macrolocationbind
- 3984640
+ 4036896
id
- 2778886
+ 2826220
location
- 1988454
+ 2017729
@@ -12801,22 +12625,22 @@
1
2
- 2183104
+ 2225956
2
3
- 336443
+ 340592
3
7
- 229815
+ 230144
7
57
- 29523
+ 29528
@@ -12832,22 +12656,22 @@
1
2
- 1589588
+ 1608672
2
3
- 169647
+ 177047
3
8
- 154233
+ 156640
8
723
- 74984
+ 75368
@@ -12857,19 +12681,19 @@
macro_argument_unexpanded
- 86176782
+ 85909146
invocation
- 26568343
+ 26486648
argument_index
- 763
+ 760
text
- 326094
+ 325037
@@ -12883,22 +12707,22 @@
1
2
- 7436793
+ 7413109
2
3
- 10861692
+ 10827242
3
4
- 6256808
+ 6239771
4
67
- 2013048
+ 2006525
@@ -12914,22 +12738,22 @@
1
2
- 7508010
+ 7484094
2
3
- 11011561
+ 10976626
3
4
- 6087232
+ 6070745
4
67
- 1961538
+ 1955182
@@ -12945,7 +12769,7 @@
41230
41231
- 670
+ 668
41432
@@ -12953,8 +12777,8 @@
57
- 715085
- 2297335
+ 715366
+ 2297717
34
@@ -12971,7 +12795,7 @@
2
3
- 670
+ 668
13
@@ -12997,57 +12821,57 @@
1
2
- 40858
+ 40726
2
3
- 65607
+ 65394
3
4
- 15184
+ 15135
4
5
- 45102
+ 44945
5
8
- 25569
+ 25510
8
12
- 16075
+ 16000
12
16
- 22297
+ 22213
16
23
- 26518
+ 26420
23
43
- 24748
+ 24691
43
- 164
- 24459
+ 165
+ 24391
- 164
+ 165
521384
- 19671
+ 19608
@@ -13063,17 +12887,17 @@
1
2
- 235830
+ 235066
2
3
- 79728
+ 79469
3
9
- 10535
+ 10501
@@ -13083,19 +12907,19 @@
macro_argument_expanded
- 86176782
+ 85909146
invocation
- 26568343
+ 26486648
argument_index
- 763
+ 760
text
- 197597
+ 196979
@@ -13109,22 +12933,22 @@
1
2
- 7436793
+ 7413109
2
3
- 10861692
+ 10827242
3
4
- 6256808
+ 6239771
4
67
- 2013048
+ 2006525
@@ -13140,22 +12964,22 @@
1
2
- 10747743
+ 10713329
2
3
- 9374139
+ 9344510
3
4
- 5306998
+ 5293039
4
9
- 1139462
+ 1135769
@@ -13171,7 +12995,7 @@
41230
41231
- 670
+ 668
41432
@@ -13179,8 +13003,8 @@
57
- 715085
- 2297335
+ 715366
+ 2297717
34
@@ -13197,7 +13021,7 @@
1
2
- 659
+ 657
2
@@ -13206,7 +13030,7 @@
870
- 13877
+ 13879
46
@@ -13223,62 +13047,62 @@
1
2
- 24552
+ 24472
2
3
- 41147
+ 41025
3
4
- 6927
+ 6904
4
5
- 16364
+ 16311
5
6
- 2995
+ 2985
6
7
- 23291
+ 23204
7
9
- 15982
+ 15953
9
15
- 16699
+ 16622
15
31
- 15589
+ 15527
31
97
- 15080
+ 15054
97
775
- 15485
+ 15446
775
- 1052916
- 3481
+ 1052972
+ 3469
@@ -13294,17 +13118,17 @@
1
2
- 99989
+ 99688
2
3
- 82850
+ 82582
3
66
- 14756
+ 14708
@@ -13314,19 +13138,19 @@
functions
- 4726273
+ 4628077
id
- 4726273
+ 4628077
name
- 1934352
+ 1902792
kind
- 3292
+ 3239
@@ -13340,7 +13164,7 @@
1
2
- 4726273
+ 4628077
@@ -13356,7 +13180,7 @@
1
2
- 4726273
+ 4628077
@@ -13372,22 +13196,22 @@
1
2
- 1516622
+ 1492704
2
3
- 154296
+ 151816
3
5
- 151003
+ 149038
5
- 1724
- 112429
+ 1692
+ 109233
@@ -13403,12 +13227,12 @@
1
2
- 1933881
+ 1902329
2
3
- 470
+ 462
@@ -13424,37 +13248,37 @@
6
7
- 470
+ 462
64
65
- 470
+ 462
173
174
- 470
+ 462
195
196
- 470
+ 462
- 1358
- 1359
- 470
+ 1348
+ 1349
+ 462
- 2432
- 2433
- 470
+ 2400
+ 2401
+ 462
- 5819
- 5820
- 470
+ 5813
+ 5814
+ 462
@@ -13470,37 +13294,37 @@
3
4
- 470
+ 462
33
34
- 470
+ 462
39
40
- 470
+ 462
94
95
- 470
+ 462
195
196
- 470
+ 462
- 244
- 245
- 470
+ 243
+ 244
+ 462
3505
3506
- 470
+ 462
@@ -13510,15 +13334,15 @@
function_entry_point
- 1176981
+ 1158060
id
- 1167103
+ 1148340
entry_point
- 1176981
+ 1158060
@@ -13532,12 +13356,12 @@
1
2
- 1157224
+ 1138620
2
3
- 9878
+ 9719
@@ -13553,7 +13377,7 @@
1
2
- 1176981
+ 1158060
@@ -13563,15 +13387,15 @@
function_return_type
- 4734741
+ 4636408
id
- 4726273
+ 4628077
return_type
- 1016569
+ 990970
@@ -13585,12 +13409,12 @@
1
2
- 4719217
+ 4621134
2
5
- 7056
+ 6942
@@ -13606,22 +13430,22 @@
1
2
- 523103
+ 512842
2
3
- 390445
+ 376763
3
- 11
- 78559
+ 10
+ 74519
- 11
- 2516
- 24461
+ 10
+ 2506
+ 26845
@@ -13639,7 +13463,7 @@
traits
- 1
+ 2
handle
@@ -13707,9 +13531,9 @@
12
- 2
- 3
- 1
+ 1
+ 2
+ 2
@@ -13723,9 +13547,9 @@
12
- 2
- 3
- 1
+ 1
+ 2
+ 2
@@ -13739,9 +13563,9 @@
12
- 2
- 3
- 1
+ 1
+ 2
+ 2
@@ -13943,48 +13767,48 @@
purefunctions
- 99446
+ 99575
id
- 99446
+ 99575
function_deleted
- 140654
+ 138393
id
- 140654
+ 138393
function_defaulted
- 74325
+ 73130
id
- 74325
+ 73130
member_function_this_type
- 553641
+ 554460
id
- 553641
+ 554460
this_type
- 189690
+ 189971
@@ -13998,7 +13822,7 @@
1
2
- 553641
+ 554460
@@ -14014,32 +13838,32 @@
1
2
- 68526
+ 68628
2
3
- 45414
+ 45481
3
4
- 30475
+ 30521
4
5
- 15537
+ 15560
5
7
- 15607
+ 15631
7
66
- 14128
+ 14149
@@ -14049,27 +13873,27 @@
fun_decls
- 5102136
+ 5000674
id
- 5096962
+ 4995583
function
- 4578563
+ 4485518
type_id
- 1013276
+ 989581
name
- 1836035
+ 1806056
location
- 3461324
+ 3404291
@@ -14083,7 +13907,7 @@
1
2
- 5096962
+ 4995583
@@ -14099,12 +13923,12 @@
1
2
- 5091787
+ 4990491
2
3
- 5174
+ 5091
@@ -14120,7 +13944,7 @@
1
2
- 5096962
+ 4995583
@@ -14136,7 +13960,7 @@
1
2
- 5096962
+ 4995583
@@ -14152,17 +13976,17 @@
1
2
- 4141075
+ 4055063
2
3
- 363161
+ 357323
3
7
- 74325
+ 73130
@@ -14178,12 +14002,12 @@
1
2
- 4536696
+ 4444324
2
5
- 41867
+ 41194
@@ -14199,7 +14023,7 @@
1
2
- 4578563
+ 4485518
@@ -14215,17 +14039,17 @@
1
2
- 4197996
+ 4111069
2
4
- 379155
+ 373060
4
6
- 1411
+ 1388
@@ -14241,22 +14065,22 @@
1
2
- 445954
+ 438785
2
3
- 453481
+ 438785
3
- 9
- 79500
+ 8
+ 74519
- 9
- 2768
- 34340
+ 8
+ 2758
+ 37491
@@ -14272,22 +14096,22 @@
1
2
- 530629
+ 522099
2
3
- 381978
+ 368431
3
11
- 77148
+ 75908
11
- 2477
- 23520
+ 2467
+ 23142
@@ -14303,17 +14127,17 @@
1
2
- 883912
+ 862297
2
5
- 90319
+ 88867
5
- 822
- 39044
+ 821
+ 38416
@@ -14329,22 +14153,22 @@
1
2
- 779480
+ 759543
2
3
- 133127
+ 130987
3
11
- 78089
+ 76833
11
- 2030
- 22579
+ 2029
+ 22216
@@ -14360,27 +14184,27 @@
1
2
- 1245192
+ 1225174
2
3
- 269548
+ 265215
3
4
- 80441
+ 79148
4
6
- 138772
+ 136541
6
- 1758
- 102080
+ 1726
+ 99976
@@ -14396,22 +14220,22 @@
1
2
- 1425832
+ 1402910
2
3
- 153355
+ 150890
3
5
- 145358
+ 143021
5
- 1708
- 111488
+ 1676
+ 109233
@@ -14427,17 +14251,17 @@
1
2
- 1615880
+ 1589440
2
4
- 135009
+ 132839
4
- 954
- 85145
+ 938
+ 83776
@@ -14453,27 +14277,27 @@
1
2
- 1266831
+ 1246002
2
3
- 296362
+ 291598
3
4
- 79500
+ 78222
4
8
- 139243
+ 137004
8
- 664
- 54097
+ 661
+ 53228
@@ -14489,17 +14313,17 @@
1
2
- 2995611
+ 2947454
2
4
- 302007
+ 297152
4
55
- 163704
+ 159684
@@ -14515,17 +14339,17 @@
1
2
- 3063351
+ 3014105
2
6
- 268607
+ 264289
6
55
- 129364
+ 125896
@@ -14541,12 +14365,12 @@
1
2
- 3245873
+ 3193692
2
27
- 215450
+ 210598
@@ -14562,12 +14386,12 @@
1
2
- 3285388
+ 3231646
2
13
- 175935
+ 172644
@@ -14577,48 +14401,48 @@
fun_def
- 1963988
+ 1932415
id
- 1963988
+ 1932415
fun_specialized
- 26343
+ 25919
id
- 26343
+ 25919
fun_implicit
- 198
+ 199
id
- 198
+ 199
fun_decl_specifiers
- 2937280
+ 2890060
id
- 1710904
+ 1683400
name
- 2822
+ 2777
@@ -14632,17 +14456,17 @@
1
2
- 503345
+ 495253
2
3
- 1188742
+ 1169632
3
4
- 18816
+ 18514
@@ -14658,32 +14482,32 @@
50
51
- 470
+ 462
203
204
- 470
+ 462
209
210
- 470
+ 462
657
658
- 470
+ 462
2561
2562
- 470
+ 462
2564
2565
- 470
+ 462
@@ -14814,26 +14638,26 @@
fun_decl_empty_throws
- 1978101
+ 1926861
fun_decl
- 1978101
+ 1926861
fun_decl_noexcept
- 61239
+ 61185
fun_decl
- 61239
+ 61185
constant
- 61135
+ 61080
@@ -14847,7 +14671,7 @@
1
2
- 61239
+ 61185
@@ -14863,7 +14687,7 @@
1
2
- 61030
+ 60976
2
@@ -14878,22 +14702,22 @@
fun_decl_empty_noexcept
- 888146
+ 873868
fun_decl
- 888146
+ 873868
fun_decl_typedef_type
- 2891
+ 2888
fun_decl
- 2891
+ 2888
typedeftype_id
@@ -14911,7 +14735,7 @@
1
2
- 2891
+ 2888
@@ -14927,7 +14751,7 @@
1
2
- 42
+ 41
2
@@ -14987,19 +14811,19 @@
param_decl_bind
- 7472094
+ 7337161
id
- 7472094
+ 7337161
index
- 7997
+ 7868
fun_decl
- 4286434
+ 4202714
@@ -15013,7 +14837,7 @@
1
2
- 7472094
+ 7337161
@@ -15029,7 +14853,7 @@
1
2
- 7472094
+ 7337161
@@ -15045,72 +14869,72 @@
2
3
- 940
+ 925
5
6
- 470
+ 462
7
8
- 470
+ 462
10
11
- 940
+ 925
11
12
- 470
+ 462
12
13
- 940
+ 925
13
14
- 470
+ 462
25
26
- 470
+ 462
78
79
- 470
+ 462
245
246
- 470
+ 462
636
637
- 470
+ 462
1713
1714
- 470
+ 462
3991
3992
- 470
+ 462
- 9112
- 9113
- 470
+ 9080
+ 9081
+ 462
@@ -15126,72 +14950,72 @@
2
3
- 940
+ 925
5
6
- 470
+ 462
7
8
- 470
+ 462
10
11
- 940
+ 925
11
12
- 470
+ 462
12
13
- 940
+ 925
13
14
- 470
+ 462
25
26
- 470
+ 462
78
79
- 470
+ 462
245
246
- 470
+ 462
636
637
- 470
+ 462
1713
1714
- 470
+ 462
3991
3992
- 470
+ 462
- 9112
- 9113
- 470
+ 9080
+ 9081
+ 462
@@ -15207,22 +15031,22 @@
1
2
- 2409002
+ 2355464
2
3
- 1071608
+ 1054381
3
4
- 506638
+ 498493
4
18
- 299184
+ 294375
@@ -15238,22 +15062,22 @@
1
2
- 2409002
+ 2355464
2
3
- 1071608
+ 1054381
3
4
- 506638
+ 498493
4
18
- 299184
+ 294375
@@ -15263,27 +15087,27 @@
var_decls
- 8611913
+ 8458657
id
- 8543232
+ 8391080
variable
- 7520077
+ 7384372
type_id
- 2430641
+ 2376755
name
- 672695
+ 661881
location
- 5365099
+ 5278849
@@ -15297,7 +15121,7 @@
1
2
- 8543232
+ 8391080
@@ -15313,12 +15137,12 @@
1
2
- 8474552
+ 8323503
2
3
- 68680
+ 67576
@@ -15334,7 +15158,7 @@
1
2
- 8543232
+ 8391080
@@ -15350,7 +15174,7 @@
1
2
- 8543232
+ 8391080
@@ -15366,17 +15190,17 @@
1
2
- 6658274
+ 6536424
2
3
- 707035
+ 695669
3
7
- 154767
+ 152278
@@ -15392,12 +15216,12 @@
1
2
- 7346963
+ 7214042
2
4
- 173113
+ 170330
@@ -15413,12 +15237,12 @@
1
2
- 7402943
+ 7269122
2
3
- 117133
+ 115250
@@ -15434,12 +15258,12 @@
1
2
- 6967337
+ 6840519
2
4
- 552739
+ 543853
@@ -15455,27 +15279,27 @@
1
2
- 1505802
+ 1466784
2
3
- 516046
+ 507750
3
4
- 98787
+ 97199
4
7
- 188636
+ 185604
7
780
- 121367
+ 119416
@@ -15491,22 +15315,22 @@
1
2
- 1640342
+ 1599160
2
3
- 491114
+ 483219
3
7
- 188166
+ 185141
7
742
- 111018
+ 109233
@@ -15522,17 +15346,17 @@
1
2
- 1918828
+ 1873170
2
3
- 388563
+ 382317
3
128
- 123249
+ 121267
@@ -15548,22 +15372,22 @@
1
2
- 1743833
+ 1700988
2
3
- 406910
+ 400368
3
8
- 190048
+ 186993
8
595
- 89849
+ 88405
@@ -15579,37 +15403,37 @@
1
2
- 343874
+ 338346
2
3
- 87497
+ 86090
3
4
- 48923
+ 48136
4
6
- 52216
+ 51376
6
12
- 52686
+ 51839
12
33
- 50804
+ 49988
34
- 3281
- 36692
+ 3249
+ 36102
@@ -15625,37 +15449,37 @@
1
2
- 371628
+ 365654
2
3
- 78559
+ 77296
3
4
- 45630
+ 44896
4
6
- 49864
+ 49062
6
14
- 53627
+ 52765
14
56
- 51275
+ 50451
56
- 3198
- 22109
+ 3166
+ 21754
@@ -15671,27 +15495,27 @@
1
2
- 460537
+ 453134
2
3
- 94553
+ 93033
3
5
- 47041
+ 46285
5
19
- 51275
+ 50451
19
- 1979
- 19287
+ 1947
+ 18977
@@ -15707,32 +15531,32 @@
1
2
- 381978
+ 375837
2
3
- 91260
+ 89793
3
5
- 60213
+ 59245
5
9
- 51745
+ 50913
9
21
- 50804
+ 49988
21
1020
- 36692
+ 36102
@@ -15748,17 +15572,17 @@
1
2
- 4535755
+ 4462838
2
3
- 550387
+ 541539
3
- 1783
- 278956
+ 1751
+ 274472
@@ -15774,17 +15598,17 @@
1
2
- 4939842
+ 4860429
2
17
- 414436
+ 407774
17
- 1779
- 10819
+ 1747
+ 10645
@@ -15800,12 +15624,12 @@
1
2
- 5016520
+ 4935875
2
- 1561
- 348578
+ 1529
+ 342974
@@ -15821,12 +15645,12 @@
1
2
- 5360865
+ 5274684
2
24
- 4233
+ 4165
@@ -15836,26 +15660,26 @@
var_def
- 4083685
+ 4018035
id
- 4083685
+ 4018035
var_decl_specifiers
- 334936
+ 329552
id
- 334936
+ 329552
name
- 1411
+ 1388
@@ -15869,7 +15693,7 @@
1
2
- 334936
+ 329552
@@ -15885,17 +15709,17 @@
15
16
- 470
+ 462
66
67
- 470
+ 462
631
632
- 470
+ 462
@@ -15916,19 +15740,19 @@
type_decls
- 3283977
+ 3240440
id
- 3283977
+ 3240440
type_id
- 3233172
+ 3190452
location
- 3204006
+ 3161755
@@ -15942,7 +15766,7 @@
1
2
- 3283977
+ 3240440
@@ -15958,7 +15782,7 @@
1
2
- 3283977
+ 3240440
@@ -15974,12 +15798,12 @@
1
2
- 3191305
+ 3149258
2
5
- 41867
+ 41194
@@ -15995,12 +15819,12 @@
1
2
- 3191305
+ 3149258
2
5
- 41867
+ 41194
@@ -16016,12 +15840,12 @@
1
2
- 3163080
+ 3121487
2
20
- 40926
+ 40268
@@ -16037,12 +15861,12 @@
1
2
- 3163080
+ 3121487
2
20
- 40926
+ 40268
@@ -16052,45 +15876,45 @@
type_def
- 2660675
+ 2627159
id
- 2660675
+ 2627159
type_decl_top
- 755959
+ 743806
type_decl
- 755959
+ 743806
namespace_decls
- 306972
+ 307432
id
- 306972
+ 307432
namespace_id
- 1414
+ 1416
location
- 306972
+ 307432
bodylocation
- 306972
+ 307432
@@ -16104,7 +15928,7 @@
1
2
- 306972
+ 307432
@@ -16120,7 +15944,7 @@
1
2
- 306972
+ 307432
@@ -16136,7 +15960,7 @@
1
2
- 306972
+ 307432
@@ -16167,12 +15991,12 @@
6
14
- 106
+ 107
14
30
- 106
+ 107
30
@@ -16187,21 +16011,21 @@
80
127
- 106
+ 107
129
199
- 106
+ 107
201
504
- 106
+ 107
512
- 12128
+ 12133
69
@@ -16233,12 +16057,12 @@
6
14
- 106
+ 107
14
30
- 106
+ 107
30
@@ -16253,21 +16077,21 @@
80
127
- 106
+ 107
129
199
- 106
+ 107
201
504
- 106
+ 107
512
- 12128
+ 12133
69
@@ -16299,12 +16123,12 @@
6
14
- 106
+ 107
14
30
- 106
+ 107
30
@@ -16319,21 +16143,21 @@
80
127
- 106
+ 107
129
199
- 106
+ 107
201
504
- 106
+ 107
512
- 12128
+ 12133
69
@@ -16350,7 +16174,7 @@
1
2
- 306972
+ 307432
@@ -16366,7 +16190,7 @@
1
2
- 306972
+ 307432
@@ -16382,7 +16206,7 @@
1
2
- 306972
+ 307432
@@ -16398,7 +16222,7 @@
1
2
- 306972
+ 307432
@@ -16414,7 +16238,7 @@
1
2
- 306972
+ 307432
@@ -16430,7 +16254,7 @@
1
2
- 306972
+ 307432
@@ -16440,19 +16264,19 @@
usings
- 374921
+ 369357
id
- 374921
+ 369357
element_id
- 318471
+ 313815
location
- 249791
+ 246238
@@ -16466,7 +16290,7 @@
1
2
- 374921
+ 369357
@@ -16482,7 +16306,7 @@
1
2
- 374921
+ 369357
@@ -16498,17 +16322,17 @@
1
2
- 263903
+ 260123
2
3
- 53157
+ 52302
3
5
- 1411
+ 1388
@@ -16524,17 +16348,17 @@
1
2
- 263903
+ 260123
2
3
- 53157
+ 52302
3
5
- 1411
+ 1388
@@ -16550,22 +16374,22 @@
1
2
- 203690
+ 200878
2
4
- 11289
+ 11108
4
5
- 31517
+ 31011
5
11
- 3292
+ 3239
@@ -16581,22 +16405,22 @@
1
2
- 203690
+ 200878
2
4
- 11289
+ 11108
4
5
- 31517
+ 31011
5
11
- 3292
+ 3239
@@ -16606,15 +16430,15 @@
using_container
- 478195
+ 476668
parent
- 11298
+ 11285
child
- 303207
+ 302247
@@ -16628,42 +16452,42 @@
1
2
- 3353
+ 3365
2
4
- 959
+ 956
4
6
- 427
+ 426
6
7
- 2555
+ 2547
7
17
- 925
+ 922
19
143
- 786
+ 783
178
179
- 1329
+ 1325
179
183
- 878
+ 876
201
@@ -16684,22 +16508,22 @@
1
2
- 223629
+ 222928
2
3
- 52990
+ 52818
3
11
- 24401
+ 24322
13
41
- 2185
+ 2178
@@ -16709,27 +16533,27 @@
static_asserts
- 130417
+ 130562
id
- 130417
+ 130562
condition
- 130417
+ 130562
message
- 29456
+ 29488
location
- 16774
+ 16793
enclosing
- 1942
+ 1944
@@ -16743,7 +16567,7 @@
1
2
- 130417
+ 130562
@@ -16759,7 +16583,7 @@
1
2
- 130417
+ 130562
@@ -16775,7 +16599,7 @@
1
2
- 130417
+ 130562
@@ -16791,7 +16615,7 @@
1
2
- 130417
+ 130562
@@ -16807,7 +16631,7 @@
1
2
- 130417
+ 130562
@@ -16823,7 +16647,7 @@
1
2
- 130417
+ 130562
@@ -16839,7 +16663,7 @@
1
2
- 130417
+ 130562
@@ -16855,7 +16679,7 @@
1
2
- 130417
+ 130562
@@ -16871,7 +16695,7 @@
1
2
- 21949
+ 21973
2
@@ -16881,22 +16705,22 @@
3
4
- 2766
+ 2769
4
11
- 1420
+ 1422
12
17
- 2376
+ 2379
17
513
- 540
+ 541
@@ -16912,7 +16736,7 @@
1
2
- 21949
+ 21973
2
@@ -16922,22 +16746,22 @@
3
4
- 2766
+ 2769
4
11
- 1420
+ 1422
12
17
- 2376
+ 2379
17
513
- 540
+ 541
@@ -16953,12 +16777,12 @@
1
2
- 27343
+ 27373
2
33
- 2112
+ 2114
@@ -16974,7 +16798,7 @@
1
2
- 23363
+ 23389
2
@@ -16984,17 +16808,17 @@
3
4
- 2565
+ 2568
4
11
- 1263
+ 1265
12
21
- 2074
+ 2077
@@ -17010,22 +16834,22 @@
1
2
- 3131
+ 3134
2
3
- 2697
+ 2700
3
4
- 1307
+ 1309
5
6
- 3621
+ 3625
6
@@ -17035,7 +16859,7 @@
14
15
- 2049
+ 2051
16
@@ -17045,12 +16869,12 @@
17
18
- 3401
+ 3405
19
52
- 345
+ 346
@@ -17066,22 +16890,22 @@
1
2
- 3131
+ 3134
2
3
- 2697
+ 2700
3
4
- 1307
+ 1309
5
6
- 3621
+ 3625
6
@@ -17091,7 +16915,7 @@
14
15
- 2049
+ 2051
16
@@ -17101,12 +16925,12 @@
17
18
- 3401
+ 3405
19
52
- 345
+ 346
@@ -17122,17 +16946,17 @@
1
2
- 4627
+ 4632
2
3
- 5941
+ 5948
3
4
- 6023
+ 6029
4
@@ -17153,22 +16977,22 @@
1
2
- 3734
+ 3738
2
3
- 6111
+ 6118
3
4
- 1081
+ 1082
4
5
- 3590
+ 3594
5
@@ -17178,7 +17002,7 @@
13
14
- 2049
+ 2051
16
@@ -17199,7 +17023,7 @@
1
2
- 1370
+ 1372
2
@@ -17235,7 +17059,7 @@
1
2
- 1370
+ 1372
2
@@ -17271,12 +17095,12 @@
1
2
- 1540
+ 1542
2
5
- 150
+ 151
5
@@ -17302,7 +17126,7 @@
1
2
- 1527
+ 1529
2
@@ -17327,23 +17151,23 @@
params
- 6825742
+ 6699348
id
- 6660155
+ 6536424
function
- 3940208
+ 3860202
index
- 7997
+ 7868
type_id
- 2234478
+ 2182819
@@ -17357,7 +17181,7 @@
1
2
- 6660155
+ 6536424
@@ -17373,7 +17197,7 @@
1
2
- 6660155
+ 6536424
@@ -17389,12 +17213,12 @@
1
2
- 6535025
+ 6413305
2
4
- 125130
+ 123119
@@ -17410,22 +17234,22 @@
1
2
- 2303158
+ 2249470
2
3
- 960590
+ 945147
3
4
- 433253
+ 426288
4
18
- 243205
+ 239295
@@ -17441,22 +17265,22 @@
1
2
- 2303158
+ 2249470
2
3
- 960590
+ 945147
3
4
- 433253
+ 426288
4
18
- 243205
+ 239295
@@ -17472,22 +17296,22 @@
1
2
- 2605636
+ 2547085
2
3
- 831225
+ 817863
3
4
- 349519
+ 343900
4
12
- 153826
+ 151353
@@ -17503,72 +17327,72 @@
2
3
- 940
+ 925
4
5
- 470
+ 462
6
7
- 470
+ 462
8
9
- 940
+ 925
9
10
- 470
+ 462
10
11
- 940
+ 925
11
12
- 470
+ 462
19
20
- 470
+ 462
64
65
- 470
+ 462
194
195
- 470
+ 462
517
518
- 470
+ 462
1438
1439
- 470
+ 462
3480
3481
- 470
+ 462
- 8376
- 8377
- 470
+ 8340
+ 8341
+ 462
@@ -17584,72 +17408,72 @@
2
3
- 940
+ 925
4
5
- 470
+ 462
6
7
- 470
+ 462
8
9
- 940
+ 925
9
10
- 470
+ 462
10
11
- 940
+ 925
11
12
- 470
+ 462
19
20
- 470
+ 462
64
65
- 470
+ 462
194
195
- 470
+ 462
517
518
- 470
+ 462
1438
1439
- 470
+ 462
3480
3481
- 470
+ 462
- 8376
- 8377
- 470
+ 8340
+ 8341
+ 462
@@ -17665,67 +17489,67 @@
1
2
- 940
+ 925
3
4
- 470
+ 462
4
5
- 470
+ 462
5
6
- 470
+ 462
6
7
- 1411
+ 1388
7
8
- 940
+ 925
11
12
- 470
+ 462
42
43
- 470
+ 462
106
107
- 470
+ 462
228
229
- 470
+ 462
582
583
- 470
+ 462
1275
1276
- 470
+ 462
- 3666
- 3667
- 470
+ 3632
+ 3633
+ 462
@@ -17741,22 +17565,22 @@
1
2
- 1525560
+ 1485298
2
3
- 446425
+ 439248
3
8
- 171701
+ 168941
8
- 522
- 90790
+ 520
+ 89330
@@ -17772,22 +17596,22 @@
1
2
- 1749008
+ 1705154
2
3
- 250731
+ 246701
3
9
- 169820
+ 167090
9
- 506
- 64917
+ 504
+ 63873
@@ -17803,17 +17627,17 @@
1
2
- 1801694
+ 1756993
2
3
- 353282
+ 347603
3
13
- 79500
+ 78222
@@ -17823,15 +17647,15 @@
overrides
- 159823
+ 160001
new
- 125023
+ 125162
old
- 15095
+ 15112
@@ -17845,12 +17669,12 @@
1
2
- 90229
+ 90329
2
3
- 34787
+ 34826
3
@@ -17871,37 +17695,37 @@
1
2
- 7922
+ 7930
2
3
- 1905
+ 1907
3
4
- 987
+ 988
4
5
- 1320
+ 1321
5
11
- 1213
+ 1214
11
60
- 1163
+ 1164
61
231
- 584
+ 585
@@ -17911,19 +17735,19 @@
membervariables
- 1051873
+ 1054757
id
- 1050083
+ 1052962
type_id
- 326293
+ 327188
name
- 449643
+ 450876
@@ -17937,12 +17761,12 @@
1
2
- 1048372
+ 1051247
2
4
- 1710
+ 1715
@@ -17958,7 +17782,7 @@
1
2
- 1050083
+ 1052962
@@ -17974,22 +17798,22 @@
1
2
- 241965
+ 242629
2
3
- 51670
+ 51812
3
10
- 25417
+ 25487
10
4152
- 7239
+ 7259
@@ -18005,22 +17829,22 @@
1
2
- 254137
+ 254834
2
3
- 46261
+ 46387
3
40
- 24502
+ 24570
41
2031
- 1392
+ 1396
@@ -18036,22 +17860,22 @@
1
2
- 294034
+ 294840
2
3
- 86157
+ 86394
3
5
- 41010
+ 41122
5
646
- 28440
+ 28518
@@ -18067,17 +17891,17 @@
1
2
- 366230
+ 367234
2
3
- 51511
+ 51652
3
650
- 31901
+ 31988
@@ -18258,19 +18082,19 @@
localvariables
- 581690
+ 581169
id
- 581690
+ 581169
type_id
- 37905
+ 37871
name
- 91402
+ 91320
@@ -18284,7 +18108,7 @@
1
2
- 581690
+ 581169
@@ -18300,7 +18124,7 @@
1
2
- 581690
+ 581169
@@ -18316,32 +18140,32 @@
1
2
- 21207
+ 21188
2
3
- 5413
+ 5408
3
4
- 2479
+ 2477
4
7
- 3412
+ 3409
7
18
- 2878
+ 2876
18
15847
- 2513
+ 2511
@@ -18357,22 +18181,22 @@
1
2
- 26994
+ 26970
2
3
- 4606
+ 4602
3
5
- 2946
+ 2943
5
31
- 2845
+ 2842
31
@@ -18393,27 +18217,27 @@
1
2
- 57570
+ 57518
2
3
- 14419
+ 14406
3
5
- 8388
+ 8381
5
15
- 7048
+ 7041
15
5176
- 3975
+ 3972
@@ -18429,17 +18253,17 @@
1
2
- 77214
+ 77144
2
3
- 7481
+ 7474
3
1486
- 6707
+ 6701
@@ -18449,15 +18273,15 @@
autoderivation
- 149488
+ 149355
var
- 149488
+ 149355
derivation_type
- 523
+ 522
@@ -18471,7 +18295,7 @@
1
2
- 149488
+ 149355
@@ -18517,19 +18341,19 @@
enumconstants
- 240613
+ 241273
id
- 240613
+ 241273
parent
- 28401
+ 28478
index
- 10183
+ 10210
type_id
@@ -18537,11 +18361,11 @@
name
- 240334
+ 240994
location
- 220605
+ 221210
@@ -18555,7 +18379,7 @@
1
2
- 240613
+ 241273
@@ -18571,7 +18395,7 @@
1
2
- 240613
+ 241273
@@ -18587,7 +18411,7 @@
1
2
- 240613
+ 241273
@@ -18603,7 +18427,7 @@
1
2
- 240613
+ 241273
@@ -18619,7 +18443,7 @@
1
2
- 240613
+ 241273
@@ -18635,57 +18459,57 @@
1
2
- 994
+ 997
2
3
- 4017
+ 4028
3
4
- 5767
+ 5783
4
5
- 3898
+ 3908
5
6
- 3062
+ 3071
6
7
- 1829
+ 1834
7
8
- 1471
+ 1475
8
11
- 2585
+ 2592
11
17
- 2346
+ 2353
17
84
- 2147
+ 2153
94
257
- 278
+ 279
@@ -18701,57 +18525,57 @@
1
2
- 994
+ 997
2
3
- 4017
+ 4028
3
4
- 5767
+ 5783
4
5
- 3898
+ 3908
5
6
- 3062
+ 3071
6
7
- 1829
+ 1834
7
8
- 1471
+ 1475
8
11
- 2585
+ 2592
11
17
- 2346
+ 2353
17
84
- 2147
+ 2153
94
257
- 278
+ 279
@@ -18767,7 +18591,7 @@
1
2
- 28401
+ 28478
@@ -18783,57 +18607,57 @@
1
2
- 994
+ 997
2
3
- 4017
+ 4028
3
4
- 5767
+ 5783
4
5
- 3898
+ 3908
5
6
- 3062
+ 3071
6
7
- 1829
+ 1834
7
8
- 1471
+ 1475
8
11
- 2585
+ 2592
11
17
- 2346
+ 2353
17
84
- 2147
+ 2153
94
257
- 278
+ 279
@@ -18849,52 +18673,52 @@
1
2
- 1431
+ 1435
2
3
- 4176
+ 4188
3
4
- 5807
+ 5823
4
5
- 3858
+ 3868
5
6
- 3062
+ 3071
6
7
- 1789
+ 1794
7
8
- 1392
+ 1396
8
11
- 2505
+ 2512
11
17
- 2227
+ 2233
17
257
- 2147
+ 2153
@@ -18910,47 +18734,47 @@
1
2
- 2028
+ 2034
2
3
- 1630
+ 1635
3
4
- 1750
+ 1755
4
5
- 875
+ 877
5
9
- 795
+ 797
9
12
- 835
+ 837
12
20
- 875
+ 877
20
69
- 795
+ 797
77
715
- 596
+ 598
@@ -18966,47 +18790,47 @@
1
2
- 2028
+ 2034
2
3
- 1630
+ 1635
3
4
- 1750
+ 1755
4
5
- 875
+ 877
5
9
- 795
+ 797
9
12
- 835
+ 837
12
20
- 875
+ 877
20
69
- 795
+ 797
77
715
- 596
+ 598
@@ -19022,7 +18846,7 @@
1
2
- 10183
+ 10210
@@ -19038,47 +18862,47 @@
1
2
- 2028
+ 2034
2
3
- 1630
+ 1635
3
4
- 1750
+ 1755
4
5
- 875
+ 877
5
9
- 795
+ 797
9
12
- 835
+ 837
12
20
- 875
+ 877
20
69
- 795
+ 797
77
712
- 596
+ 598
@@ -19094,47 +18918,47 @@
1
2
- 2028
+ 2034
2
3
- 1630
+ 1635
3
4
- 1750
+ 1755
4
5
- 875
+ 877
5
9
- 795
+ 797
9
12
- 835
+ 837
12
20
- 875
+ 877
20
69
- 795
+ 797
77
715
- 596
+ 598
@@ -19230,12 +19054,12 @@
1
2
- 240056
+ 240714
2
3
- 278
+ 279
@@ -19251,12 +19075,12 @@
1
2
- 240056
+ 240714
2
3
- 278
+ 279
@@ -19272,7 +19096,7 @@
1
2
- 240334
+ 240994
@@ -19288,7 +19112,7 @@
1
2
- 240334
+ 240994
@@ -19304,12 +19128,12 @@
1
2
- 240056
+ 240714
2
3
- 278
+ 279
@@ -19325,12 +19149,12 @@
1
2
- 219849
+ 220452
2
205
- 755
+ 757
@@ -19346,7 +19170,7 @@
1
2
- 220605
+ 221210
@@ -19362,12 +19186,12 @@
1
2
- 219849
+ 220452
2
205
- 755
+ 757
@@ -19383,7 +19207,7 @@
1
2
- 220605
+ 221210
@@ -19399,12 +19223,12 @@
1
2
- 219849
+ 220452
2
205
- 755
+ 757
@@ -19414,31 +19238,31 @@
builtintypes
- 22109
+ 21754
id
- 22109
+ 21754
name
- 22109
+ 21754
kind
- 22109
+ 21754
size
- 3292
+ 3239
sign
- 1411
+ 1388
alignment
- 2352
+ 2314
@@ -19452,7 +19276,7 @@
1
2
- 22109
+ 21754
@@ -19468,7 +19292,7 @@
1
2
- 22109
+ 21754
@@ -19484,7 +19308,7 @@
1
2
- 22109
+ 21754
@@ -19500,7 +19324,7 @@
1
2
- 22109
+ 21754
@@ -19516,7 +19340,7 @@
1
2
- 22109
+ 21754
@@ -19532,7 +19356,7 @@
1
2
- 22109
+ 21754
@@ -19548,7 +19372,7 @@
1
2
- 22109
+ 21754
@@ -19564,7 +19388,7 @@
1
2
- 22109
+ 21754
@@ -19580,7 +19404,7 @@
1
2
- 22109
+ 21754
@@ -19596,7 +19420,7 @@
1
2
- 22109
+ 21754
@@ -19612,7 +19436,7 @@
1
2
- 22109
+ 21754
@@ -19628,7 +19452,7 @@
1
2
- 22109
+ 21754
@@ -19644,7 +19468,7 @@
1
2
- 22109
+ 21754
@@ -19660,7 +19484,7 @@
1
2
- 22109
+ 21754
@@ -19676,7 +19500,7 @@
1
2
- 22109
+ 21754
@@ -19692,37 +19516,37 @@
1
2
- 470
+ 462
2
3
- 470
+ 462
4
5
- 470
+ 462
7
8
- 470
+ 462
9
10
- 470
+ 462
11
12
- 470
+ 462
13
14
- 470
+ 462
@@ -19738,37 +19562,37 @@
1
2
- 470
+ 462
2
3
- 470
+ 462
4
5
- 470
+ 462
7
8
- 470
+ 462
9
10
- 470
+ 462
11
12
- 470
+ 462
13
14
- 470
+ 462
@@ -19784,37 +19608,37 @@
1
2
- 470
+ 462
2
3
- 470
+ 462
4
5
- 470
+ 462
7
8
- 470
+ 462
9
10
- 470
+ 462
11
12
- 470
+ 462
13
14
- 470
+ 462
@@ -19830,12 +19654,12 @@
1
2
- 940
+ 925
3
4
- 2352
+ 2314
@@ -19851,12 +19675,12 @@
1
2
- 2352
+ 2314
2
3
- 940
+ 925
@@ -19872,17 +19696,17 @@
6
7
- 470
+ 462
12
13
- 470
+ 462
29
30
- 470
+ 462
@@ -19898,17 +19722,17 @@
6
7
- 470
+ 462
12
13
- 470
+ 462
29
30
- 470
+ 462
@@ -19924,17 +19748,17 @@
6
7
- 470
+ 462
12
13
- 470
+ 462
29
30
- 470
+ 462
@@ -19950,12 +19774,12 @@
5
6
- 940
+ 925
7
8
- 470
+ 462
@@ -19971,7 +19795,7 @@
5
6
- 1411
+ 1388
@@ -19987,27 +19811,27 @@
4
5
- 470
+ 462
8
9
- 470
+ 462
10
11
- 470
+ 462
12
13
- 470
+ 462
13
14
- 470
+ 462
@@ -20023,27 +19847,27 @@
4
5
- 470
+ 462
8
9
- 470
+ 462
10
11
- 470
+ 462
12
13
- 470
+ 462
13
14
- 470
+ 462
@@ -20059,27 +19883,27 @@
4
5
- 470
+ 462
8
9
- 470
+ 462
10
11
- 470
+ 462
12
13
- 470
+ 462
13
14
- 470
+ 462
@@ -20095,12 +19919,12 @@
1
2
- 470
+ 462
2
3
- 1881
+ 1851
@@ -20116,7 +19940,7 @@
3
4
- 2352
+ 2314
@@ -20126,23 +19950,23 @@
derivedtypes
- 4413446
+ 4324907
id
- 4413446
+ 4324907
name
- 2205312
+ 2161065
kind
- 2822
+ 2777
type_id
- 2729356
+ 2666964
@@ -20156,7 +19980,7 @@
1
2
- 4413446
+ 4324907
@@ -20172,7 +19996,7 @@
1
2
- 4413446
+ 4324907
@@ -20188,7 +20012,7 @@
1
2
- 4413446
+ 4324907
@@ -20204,17 +20028,17 @@
1
2
- 1935763
+ 1901404
2
5
- 171231
+ 162924
5
- 1173
- 98317
+ 1167
+ 96736
@@ -20230,12 +20054,12 @@
1
2
- 2204371
+ 2160139
2
3
- 940
+ 925
@@ -20251,17 +20075,17 @@
1
2
- 1935763
+ 1901404
2
5
- 171231
+ 162924
5
- 1155
- 98317
+ 1149
+ 96736
@@ -20275,34 +20099,34 @@
12
- 199
- 200
- 470
+ 236
+ 237
+ 462
- 1103
- 1104
- 470
+ 1085
+ 1086
+ 462
- 1154
- 1155
- 470
+ 1148
+ 1149
+ 462
- 1223
- 1224
- 470
+ 1220
+ 1221
+ 462
- 2193
- 2194
- 470
+ 2177
+ 2178
+ 462
- 3510
- 3511
- 470
+ 3478
+ 3479
+ 462
@@ -20318,32 +20142,32 @@
1
2
- 470
+ 462
- 164
- 165
- 470
+ 201
+ 202
+ 462
- 611
- 612
- 470
+ 609
+ 610
+ 462
- 783
- 784
- 470
+ 768
+ 769
+ 462
- 1149
- 1150
- 470
+ 1136
+ 1137
+ 462
- 1982
- 1983
- 470
+ 1956
+ 1957
+ 462
@@ -20359,32 +20183,32 @@
84
85
- 470
+ 462
- 1103
- 1104
- 470
+ 1085
+ 1086
+ 462
- 1154
- 1155
- 470
+ 1148
+ 1149
+ 462
- 1223
- 1224
- 470
+ 1220
+ 1221
+ 462
- 2148
- 2149
- 470
+ 2132
+ 2133
+ 462
- 3510
- 3511
- 470
+ 3478
+ 3479
+ 462
@@ -20400,22 +20224,22 @@
1
2
- 1685972
+ 1649148
2
3
- 568733
+ 558201
3
4
- 367395
+ 354083
4
- 54
- 107254
+ 72
+ 105530
@@ -20431,22 +20255,22 @@
1
2
- 1697262
+ 1660257
2
3
- 561206
+ 550796
3
4
- 364572
+ 351306
4
- 54
- 106314
+ 72
+ 104605
@@ -20462,22 +20286,22 @@
1
2
- 1690206
+ 1653314
2
3
- 572496
+ 561904
3
4
- 366454
+ 353157
4
6
- 100198
+ 98587
@@ -20487,19 +20311,19 @@
pointerishsize
- 3314342
+ 3208041
id
- 3314342
+ 3208041
size
- 35
+ 462
alignment
- 35
+ 462
@@ -20513,7 +20337,7 @@
1
2
- 3314342
+ 3208041
@@ -20529,7 +20353,7 @@
1
2
- 3314342
+ 3208041
@@ -20543,9 +20367,9 @@
12
- 94071
- 94072
- 35
+ 6931
+ 6932
+ 462
@@ -20561,7 +20385,7 @@
1
2
- 35
+ 462
@@ -20575,9 +20399,9 @@
12
- 94071
- 94072
- 35
+ 6931
+ 6932
+ 462
@@ -20593,7 +20417,7 @@
1
2
- 35
+ 462
@@ -20603,23 +20427,23 @@
arraysizes
- 71503
+ 87479
id
- 71503
+ 87479
num_elements
- 23520
+ 31474
bytesize
- 26343
+ 32862
alignment
- 1881
+ 1851
@@ -20633,7 +20457,7 @@
1
2
- 71503
+ 87479
@@ -20649,7 +20473,7 @@
1
2
- 71503
+ 87479
@@ -20665,7 +20489,7 @@
1
2
- 71503
+ 87479
@@ -20681,32 +20505,27 @@
1
2
- 2352
+ 1851
2
3
- 15053
+ 23605
3
- 4
- 1411
+ 5
+ 2777
- 4
- 6
- 1881
+ 5
+ 13
+ 2777
- 6
- 11
- 1881
-
-
- 12
+ 13
14
- 940
+ 462
@@ -20722,22 +20541,17 @@
1
2
- 18346
+ 26382
2
3
- 2352
+ 2314
3
- 4
- 1881
-
-
- 4
7
- 940
+ 2777
@@ -20753,22 +20567,17 @@
1
2
- 18346
+ 26382
2
3
- 2822
+ 2777
3
- 4
- 1411
-
-
- 4
5
- 940
+ 2314
@@ -20784,27 +20593,27 @@
1
2
- 2822
+ 1851
2
3
- 16934
+ 23605
3
4
- 3292
+ 3239
4
- 8
- 2352
+ 6
+ 2314
- 11
+ 7
16
- 940
+ 1851
@@ -20820,17 +20629,17 @@
1
2
- 21639
+ 27308
2
3
- 3292
+ 3702
3
5
- 1411
+ 1851
@@ -20846,17 +20655,17 @@
1
2
- 22109
+ 27308
2
3
- 3292
+ 4628
4
5
- 940
+ 925
@@ -20872,22 +20681,22 @@
5
6
- 470
+ 462
16
17
- 470
+ 462
31
32
- 470
+ 462
- 100
- 101
- 470
+ 137
+ 138
+ 462
@@ -20903,17 +20712,17 @@
4
5
- 470
+ 462
7
8
- 940
+ 925
- 50
- 51
- 470
+ 68
+ 69
+ 462
@@ -20929,22 +20738,22 @@
4
5
- 470
+ 462
7
8
- 470
+ 462
8
9
- 470
+ 462
- 50
- 51
- 470
+ 68
+ 69
+ 462
@@ -20954,15 +20763,15 @@
typedefbase
- 1724181
+ 1722225
id
- 1724181
+ 1722225
type_id
- 803746
+ 809049
@@ -20976,7 +20785,7 @@
1
2
- 1724181
+ 1722225
@@ -20992,22 +20801,22 @@
1
2
- 623380
+ 629268
2
3
- 84307
+ 85025
3
6
- 64497
+ 63319
6
- 5443
- 31560
+ 5437
+ 31435
@@ -21017,23 +20826,23 @@
decltypes
- 355640
+ 172290
id
- 23953
+ 17347
expr
- 355640
+ 172290
base_type
- 17181
+ 10357
parentheses_would_change_meaning
- 18
+ 19
@@ -21047,37 +20856,37 @@
1
2
- 5961
+ 5307
2
3
- 7492
+ 6436
3
- 4
- 3259
+ 5
+ 1128
- 4
- 7
- 1999
+ 5
+ 12
+ 1346
- 7
+ 12
18
- 1999
+ 1406
18
- 42
- 2017
+ 46
+ 1307
- 42
- 1767
- 1224
+ 51
+ 740
+ 415
@@ -21093,7 +20902,7 @@
1
2
- 23953
+ 17347
@@ -21109,7 +20918,7 @@
1
2
- 23953
+ 17347
@@ -21125,7 +20934,7 @@
1
2
- 355640
+ 172290
@@ -21141,7 +20950,7 @@
1
2
- 355640
+ 172290
@@ -21157,7 +20966,7 @@
1
2
- 355640
+ 172290
@@ -21173,17 +20982,17 @@
1
2
- 14479
+ 7525
2
3
- 2215
+ 2356
- 3
+ 4
149
- 486
+ 475
@@ -21199,37 +21008,37 @@
1
2
- 1800
+ 752
2
3
- 7348
+ 6376
3
4
- 3079
+ 356
4
5
- 1422
+ 1009
5
- 11
- 1368
+ 7
+ 792
- 11
- 43
- 1566
+ 7
+ 31
+ 792
- 43
- 6569
- 594
+ 31
+ 3872
+ 277
@@ -21245,7 +21054,7 @@
1
2
- 17181
+ 10357
@@ -21259,9 +21068,9 @@
12
- 1330
- 1331
- 18
+ 876
+ 877
+ 19
@@ -21275,9 +21084,9 @@
12
- 19747
- 19748
- 18
+ 8700
+ 8701
+ 19
@@ -21291,9 +21100,9 @@
12
- 954
- 955
- 18
+ 523
+ 524
+ 19
@@ -21303,19 +21112,19 @@
usertypes
- 5342989
+ 5230250
id
- 5342989
+ 5230250
name
- 1383024
+ 1349682
kind
- 5174
+ 5091
@@ -21329,7 +21138,7 @@
1
2
- 5342989
+ 5230250
@@ -21345,7 +21154,7 @@
1
2
- 5342989
+ 5230250
@@ -21361,27 +21170,27 @@
1
2
- 1001986
+ 980787
2
3
- 161352
+ 154593
3
7
- 107725
+ 104605
7
- 80
- 104432
+ 66
+ 101365
- 80
- 885
- 7526
+ 79
+ 886
+ 8331
@@ -21397,17 +21206,17 @@
1
2
- 1240488
+ 1209437
2
3
- 127012
+ 124970
3
7
- 15523
+ 15274
@@ -21423,57 +21232,57 @@
6
7
- 470
+ 462
10
11
- 470
+ 462
26
27
- 470
+ 462
124
125
- 470
+ 462
- 139
- 140
- 470
+ 136
+ 137
+ 462
- 700
- 701
- 470
+ 664
+ 665
+ 462
861
862
- 470
+ 462
963
964
- 470
+ 462
- 1762
- 1763
- 470
+ 1756
+ 1757
+ 462
- 1899
- 1900
- 470
+ 1868
+ 1869
+ 462
- 4868
- 4869
- 470
+ 4886
+ 4887
+ 462
@@ -21489,57 +21298,57 @@
5
6
- 470
+ 462
6
7
- 470
+ 462
14
15
- 470
+ 462
30
31
- 470
+ 462
44
45
- 470
+ 462
126
127
- 470
+ 462
- 269
- 270
- 470
+ 268
+ 269
+ 462
373
374
- 470
+ 462
433
434
- 470
+ 462
748
749
- 470
+ 462
- 1236
- 1237
- 470
+ 1212
+ 1213
+ 462
@@ -21549,19 +21358,19 @@
usertypesize
- 1755594
+ 1711634
id
- 1755594
+ 1711634
size
- 13642
+ 13422
alignment
- 2352
+ 2314
@@ -21575,7 +21384,7 @@
1
2
- 1755594
+ 1711634
@@ -21591,7 +21400,7 @@
1
2
- 1755594
+ 1711634
@@ -21607,47 +21416,47 @@
1
2
- 3292
+ 3239
2
3
- 4233
+ 4165
3
4
- 470
+ 462
4
5
- 940
+ 925
6
8
- 940
+ 925
9
15
- 940
+ 925
37
84
- 940
+ 925
92
163
- 940
+ 925
748
- 2539
- 940
+ 2505
+ 925
@@ -21663,17 +21472,17 @@
1
2
- 10349
+ 10182
2
3
- 2822
+ 2777
3
4
- 470
+ 462
@@ -21689,27 +21498,27 @@
2
3
- 470
+ 462
6
7
- 470
+ 462
184
185
- 470
+ 462
254
255
- 470
+ 462
- 3286
- 3287
- 470
+ 3252
+ 3253
+ 462
@@ -21725,27 +21534,27 @@
1
2
- 470
+ 462
2
3
- 470
+ 462
3
4
- 470
+ 462
9
10
- 470
+ 462
22
23
- 470
+ 462
@@ -21755,26 +21564,26 @@
usertype_final
- 9526
+ 9517
id
- 9526
+ 9517
usertype_uuid
- 36101
+ 36167
id
- 36101
+ 36167
uuid
- 35737
+ 35795
@@ -21788,7 +21597,7 @@
1
2
- 36101
+ 36167
@@ -21804,12 +21613,12 @@
1
2
- 35372
+ 35424
2
3
- 364
+ 371
@@ -21819,15 +21628,15 @@
mangled_name
- 5264430
+ 5184427
id
- 5264430
+ 5184427
mangled_name
- 1235313
+ 1244614
@@ -21841,7 +21650,7 @@
1
2
- 5264430
+ 5184427
@@ -21857,32 +21666,32 @@
1
2
- 731027
+ 754452
2
3
- 178287
+ 174495
3
4
- 84674
+ 81925
4
- 6
- 86086
+ 7
+ 110622
- 6
- 13
- 93142
+ 7
+ 26
+ 97662
- 13
- 885
- 62094
+ 26
+ 886
+ 25456
@@ -21892,59 +21701,59 @@
is_pod_class
- 554326
+ 534132
id
- 554326
+ 534132
is_standard_layout_class
- 1295997
+ 1259425
id
- 1295997
+ 1259425
is_complete
- 1694439
+ 1651463
id
- 1694439
+ 1651463
is_class_template
- 405028
+ 398517
id
- 405028
+ 398517
class_instantiation
- 1121943
+ 1092104
to
- 1121943
+ 1090870
from
- 170761
+ 70259
@@ -21958,7 +21767,12 @@
1
2
- 1121943
+ 1089729
+
+
+ 2
+ 4
+ 1141
@@ -21974,42 +21788,47 @@
1
2
- 58802
+ 20818
2
3
- 30106
+ 12772
3
4
- 16464
+ 7089
4
5
- 14582
+ 4887
5
7
- 15523
+ 5717
7
- 13
- 13171
+ 10
+ 5175
- 13
- 29
- 13171
+ 10
+ 17
+ 5475
- 30
- 84
- 8937
+ 17
+ 66
+ 5279
+
+
+ 66
+ 3994
+ 3043
@@ -22019,19 +21838,19 @@
class_template_argument
- 2978113
+ 2918536
type_id
- 1355713
+ 1329545
index
- 1295
+ 1291
arg_type
- 863386
+ 856542
@@ -22045,27 +21864,27 @@
1
2
- 551562
+ 544138
2
3
- 411593
+ 404518
3
4
- 246019
+ 235642
4
7
- 122356
+ 121141
7
113
- 24182
+ 24103
@@ -22081,22 +21900,22 @@
1
2
- 577421
+ 569833
2
3
- 424939
+ 416288
3
4
- 257884
+ 248830
4
113
- 95467
+ 94593
@@ -22117,31 +21936,31 @@
2
3
- 821
+ 818
3
26
- 104
+ 103
29
64
- 104
+ 103
69
411
- 104
+ 103
592
- 8835
- 104
+ 8747
+ 103
- 13776
- 114840
+ 12910
+ 113008
46
@@ -22163,7 +21982,7 @@
2
3
- 821
+ 818
3
@@ -22173,21 +21992,21 @@
14
26
- 104
+ 103
28
145
- 104
+ 103
195
- 4197
- 104
+ 3442
+ 103
- 10467
- 39739
+ 10455
+ 39609
34
@@ -22204,27 +22023,27 @@
1
2
- 535649
+ 533452
2
3
- 181070
+ 179089
3
4
- 52573
+ 51746
4
10
- 65688
+ 64334
10
- 11334
- 28403
+ 10167
+ 27919
@@ -22240,17 +22059,17 @@
1
2
- 755682
+ 755723
2
3
- 85741
+ 82570
3
22
- 21961
+ 18247
@@ -22260,19 +22079,19 @@
class_template_argument_value
- 508520
+ 494790
type_id
- 316590
+ 305946
index
- 1881
+ 1851
arg_value
- 508520
+ 494790
@@ -22286,17 +22105,17 @@
1
2
- 261081
+ 251329
2
3
- 53627
+ 52765
3
4
- 1881
+ 1851
@@ -22312,22 +22131,22 @@
1
2
- 200397
+ 191621
2
3
- 81852
+ 80536
3
- 5
- 29165
+ 4
+ 12034
- 5
+ 4
9
- 5174
+ 21754
@@ -22343,22 +22162,22 @@
18
19
- 470
+ 462
92
93
- 470
+ 462
- 309
- 310
- 470
+ 297
+ 298
+ 462
376
377
- 470
+ 462
@@ -22374,22 +22193,22 @@
19
20
- 470
+ 462
124
125
- 470
+ 462
- 425
- 426
- 470
+ 413
+ 414
+ 462
513
514
- 470
+ 462
@@ -22405,7 +22224,7 @@
1
2
- 508520
+ 494790
@@ -22421,7 +22240,7 @@
1
2
- 508520
+ 494790
@@ -22431,15 +22250,15 @@
is_proxy_class_for
- 65387
+ 62948
id
- 65387
+ 62948
templ_param_id
- 65387
+ 62948
@@ -22453,7 +22272,7 @@
1
2
- 65387
+ 62948
@@ -22469,7 +22288,7 @@
1
2
- 65387
+ 62948
@@ -22479,19 +22298,19 @@
type_mentions
- 4011508
+ 4022510
id
- 4011508
+ 4022510
type_id
- 197335
+ 197876
location
- 3978135
+ 3989045
kind
@@ -22509,7 +22328,7 @@
1
2
- 4011508
+ 4022510
@@ -22525,7 +22344,7 @@
1
2
- 4011508
+ 4022510
@@ -22541,7 +22360,7 @@
1
2
- 4011508
+ 4022510
@@ -22557,42 +22376,42 @@
1
2
- 97176
+ 97442
2
3
- 21638
+ 21698
3
4
- 8194
+ 8216
4
5
- 10739
+ 10769
5
7
- 14319
+ 14359
7
12
- 15791
+ 15834
12
27
- 15115
+ 15156
27
8555
- 14359
+ 14399
@@ -22608,42 +22427,42 @@
1
2
- 97176
+ 97442
2
3
- 21638
+ 21698
3
4
- 8194
+ 8216
4
5
- 10739
+ 10769
5
7
- 14319
+ 14359
7
12
- 15791
+ 15834
12
27
- 15115
+ 15156
27
8555
- 14359
+ 14399
@@ -22659,7 +22478,7 @@
1
2
- 197335
+ 197876
@@ -22675,12 +22494,12 @@
1
2
- 3944762
+ 3955580
2
3
- 33373
+ 33464
@@ -22696,12 +22515,12 @@
1
2
- 3944762
+ 3955580
2
3
- 33373
+ 33464
@@ -22717,7 +22536,7 @@
1
2
- 3978135
+ 3989045
@@ -22775,26 +22594,26 @@
is_function_template
- 1413601
+ 1390876
id
- 1413601
+ 1390876
function_instantiation
- 906422
+ 907164
to
- 906422
+ 907164
from
- 146002
+ 146148
@@ -22808,7 +22627,7 @@
1
2
- 906422
+ 907164
@@ -22824,27 +22643,27 @@
1
2
- 101152
+ 101266
2
3
- 14480
+ 14466
3
6
- 12014
+ 12032
6
21
- 12049
+ 12067
22
869
- 6306
+ 6315
@@ -22854,19 +22673,19 @@
function_template_argument
- 2339815
+ 2342325
function_id
- 1318394
+ 1319745
index
- 563
+ 564
arg_type
- 305041
+ 304999
@@ -22880,22 +22699,22 @@
1
2
- 679667
+ 680426
2
3
- 388084
+ 388305
3
4
- 179966
+ 180233
4
15
- 70676
+ 70780
@@ -22911,22 +22730,22 @@
1
2
- 694852
+ 695633
2
3
- 393404
+ 393633
3
4
- 150970
+ 151194
4
9
- 79167
+ 79284
@@ -22985,13 +22804,13 @@
35
- 17489
- 17490
+ 17479
+ 17480
35
- 34459
- 34460
+ 34442
+ 34443
35
@@ -23051,13 +22870,13 @@
35
- 2404
- 2405
+ 2397
+ 2398
35
- 5842
- 5843
+ 5835
+ 5836
35
@@ -23074,32 +22893,32 @@
1
2
- 186978
+ 187007
2
3
- 44850
+ 44670
3
5
- 23218
+ 23287
5
16
- 23535
+ 23534
16
107
- 23006
+ 23040
108
955
- 3452
+ 3457
@@ -23115,17 +22934,17 @@
1
2
- 274918
+ 274830
2
4
- 26001
+ 26039
4
17
- 4122
+ 4128
@@ -23135,19 +22954,19 @@
function_template_argument_value
- 362998
+ 363536
function_id
- 181340
+ 181609
index
- 563
+ 564
arg_value
- 360356
+ 360889
@@ -23161,12 +22980,12 @@
1
2
- 171969
+ 172223
2
8
- 9371
+ 9385
@@ -23182,17 +23001,17 @@
1
2
- 151428
+ 151652
2
3
- 20681
+ 20711
3
97
- 9230
+ 9244
@@ -23330,12 +23149,12 @@
1
2
- 357714
+ 358243
2
3
- 2642
+ 2646
@@ -23351,7 +23170,7 @@
1
2
- 360356
+ 360889
@@ -23361,26 +23180,26 @@
is_variable_template
- 42082
+ 47274
id
- 42082
+ 47274
variable_instantiation
- 49201
+ 168076
to
- 49201
+ 168076
from
- 25019
+ 25729
@@ -23394,7 +23213,7 @@
1
2
- 49201
+ 168076
@@ -23410,22 +23229,37 @@
1
2
- 14236
+ 14015
2
3
- 7746
+ 2719
3
- 8
- 1988
+ 4
+ 1359
- 8
- 14
- 1046
+ 4
+ 7
+ 1987
+
+
+ 7
+ 10
+ 2196
+
+
+ 10
+ 22
+ 1987
+
+
+ 26
+ 277
+ 1464
@@ -23435,19 +23269,19 @@
variable_template_argument
- 329648
+ 295468
variable_id
- 26380
+ 159709
index
- 1779
+ 1778
arg_type
- 217532
+ 165462
@@ -23461,22 +23295,22 @@
1
2
- 16121
+ 81894
2
3
- 5652
+ 49575
3
4
- 3873
+ 18826
4
17
- 732
+ 9413
@@ -23492,52 +23326,22 @@
1
2
- 5757
+ 85555
2
3
- 5234
+ 51876
3
4
- 2093
+ 13701
4
- 5
- 1256
-
-
- 5
- 6
- 2407
-
-
- 6
- 8
- 2303
-
-
- 8
- 11
- 2198
-
-
- 11
- 18
- 2303
-
-
- 18
- 50
- 1988
-
-
- 66
- 516
- 837
+ 17
+ 8576
@@ -23551,43 +23355,48 @@
12
- 1
- 2
+ 6
+ 7
104
- 2
- 3
- 628
+ 12
+ 13
+ 627
- 3
- 4
+ 19
+ 20
418
- 5
- 6
- 209
-
-
- 27
- 28
+ 40
+ 41
104
- 42
- 43
+ 86
+ 87
104
- 79
- 80
+ 178
+ 179
104
- 248
- 249
+ 540
+ 541
+ 104
+
+
+ 609
+ 610
+ 104
+
+
+ 1218
+ 1219
104
@@ -23607,48 +23416,43 @@
104
- 12
- 13
- 628
+ 7
+ 8
+ 627
- 13
- 14
+ 9
+ 10
+ 418
+
+
+ 26
+ 27
104
- 14
- 15
- 314
-
-
- 24
- 25
+ 45
+ 46
104
- 32
- 33
+ 127
+ 128
104
- 128
- 129
+ 372
+ 373
104
- 437
- 438
+ 388
+ 389
104
- 640
- 641
- 104
-
-
- 897
- 898
+ 729
+ 730
104
@@ -23665,17 +23469,22 @@
1
2
- 180579
+ 133352
2
3
- 21878
+ 18094
3
- 38
- 15074
+ 15
+ 12446
+
+
+ 17
+ 109
+ 1568
@@ -23691,17 +23500,17 @@
1
2
- 200259
+ 149564
2
- 5
- 16854
+ 3
+ 13805
- 5
+ 3
6
- 418
+ 2091
@@ -23711,11 +23520,11 @@
variable_template_argument_value
- 15597
+ 11818
variable_id
- 2826
+ 7739
index
@@ -23723,7 +23532,7 @@
arg_value
- 12038
+ 11818
@@ -23737,12 +23546,12 @@
1
2
- 2617
+ 7321
2
3
- 209
+ 418
@@ -23756,44 +23565,19 @@
12
- 2
- 3
- 418
+ 1
+ 2
+ 4288
- 3
- 4
- 104
+ 2
+ 3
+ 3137
4
5
- 1360
-
-
- 5
- 6
- 209
-
-
- 6
- 7
- 209
-
-
- 8
- 9
- 209
-
-
- 12
- 17
- 209
-
-
- 20
- 21
- 104
+ 313
@@ -23807,23 +23591,23 @@
12
- 2
- 3
+ 4
+ 5
104
- 6
- 7
+ 18
+ 19
104
- 8
- 9
+ 26
+ 27
104
- 13
- 14
+ 30
+ 31
104
@@ -23838,23 +23622,23 @@
12
- 12
- 13
+ 7
+ 8
104
- 30
- 31
+ 27
+ 28
104
- 33
- 34
+ 38
+ 39
104
- 40
- 41
+ 41
+ 42
104
@@ -23871,12 +23655,7 @@
1
2
- 8479
-
-
- 2
- 3
- 3559
+ 11818
@@ -23892,7 +23671,7 @@
1
2
- 12038
+ 11818
@@ -23902,15 +23681,15 @@
routinetypes
- 546982
+ 547156
id
- 546982
+ 547156
return_type
- 285945
+ 285769
@@ -23924,7 +23703,7 @@
1
2
- 546982
+ 547156
@@ -23940,17 +23719,17 @@
1
2
- 249233
+ 249002
2
3
- 21315
+ 21347
3
3594
- 15396
+ 15419
@@ -23960,19 +23739,19 @@
routinetypeargs
- 993519
+ 975696
routine
- 429019
+ 420271
index
- 7997
+ 7868
type_id
- 229563
+ 224947
@@ -23986,27 +23765,27 @@
1
2
- 155707
+ 151353
2
3
- 135479
+ 133301
3
4
- 63976
+ 62948
4
5
- 46100
+ 45359
5
18
- 27754
+ 27308
@@ -24022,27 +23801,27 @@
1
2
- 185814
+ 180975
2
3
- 135009
+ 132839
3
4
- 59272
+ 58319
4
5
- 33869
+ 33325
5
11
- 15053
+ 14811
@@ -24058,67 +23837,67 @@
2
3
- 940
+ 925
4
5
- 470
+ 462
6
7
- 470
+ 462
8
9
- 940
+ 925
9
10
- 470
+ 462
10
11
- 1411
+ 1388
13
14
- 470
+ 462
28
29
- 470
+ 462
59
60
- 470
+ 462
157
158
- 470
+ 462
293
294
- 470
+ 462
581
582
- 470
+ 462
- 912
- 913
- 470
+ 908
+ 909
+ 462
@@ -24134,57 +23913,57 @@
1
2
- 940
+ 925
3
4
- 940
+ 925
4
5
- 1411
+ 1388
5
6
- 940
+ 925
6
7
- 940
+ 925
10
11
- 470
+ 462
14
15
- 470
+ 462
47
48
- 470
+ 462
90
91
- 470
+ 462
176
177
- 470
+ 462
- 349
- 350
- 470
+ 347
+ 348
+ 462
@@ -24200,27 +23979,27 @@
1
2
- 148651
+ 145336
2
3
- 31047
+ 30548
3
5
- 16934
+ 16662
5
12
- 18346
+ 18051
12
- 113
- 14582
+ 111
+ 14348
@@ -24236,22 +24015,22 @@
1
2
- 174994
+ 171255
2
3
- 31047
+ 30548
3
6
- 18816
+ 18514
6
14
- 4704
+ 4628
@@ -24261,19 +24040,19 @@
ptrtomembers
- 38103
+ 37491
id
- 38103
+ 37491
type_id
- 38103
+ 37491
class_id
- 15523
+ 15274
@@ -24287,7 +24066,7 @@
1
2
- 38103
+ 37491
@@ -24303,7 +24082,7 @@
1
2
- 38103
+ 37491
@@ -24319,7 +24098,7 @@
1
2
- 38103
+ 37491
@@ -24335,7 +24114,7 @@
1
2
- 38103
+ 37491
@@ -24351,17 +24130,17 @@
1
2
- 13642
+ 13422
8
9
- 1411
+ 1388
28
29
- 470
+ 462
@@ -24377,17 +24156,17 @@
1
2
- 13642
+ 13422
8
9
- 1411
+ 1388
28
29
- 470
+ 462
@@ -24397,15 +24176,15 @@
specifiers
- 24932
+ 24531
id
- 24932
+ 24531
str
- 24932
+ 24531
@@ -24419,7 +24198,7 @@
1
2
- 24932
+ 24531
@@ -24435,7 +24214,7 @@
1
2
- 24932
+ 24531
@@ -24445,15 +24224,15 @@
typespecifiers
- 1317166
+ 1287196
type_id
- 1298819
+ 1269145
spec_id
- 3763
+ 3702
@@ -24467,12 +24246,12 @@
1
2
- 1280473
+ 1251094
2
3
- 18346
+ 18051
@@ -24488,42 +24267,42 @@
8
9
- 470
+ 462
36
37
- 470
+ 462
51
52
- 470
+ 462
86
87
- 470
+ 462
105
106
- 470
+ 462
219
220
- 470
+ 462
- 226
- 227
- 470
+ 223
+ 224
+ 462
- 2069
- 2070
- 470
+ 2053
+ 2054
+ 462
@@ -24533,15 +24312,15 @@
funspecifiers
- 13049498
+ 12354933
func_id
- 3975160
+ 3802185
spec_id
- 704
+ 705
@@ -24555,27 +24334,27 @@
1
2
- 314977
+ 315090
2
3
- 544551
+ 545110
3
4
- 1145438
+ 1147133
4
5
- 1732127
+ 1556434
5
8
- 238064
+ 238417
@@ -24619,8 +24398,8 @@
35
- 716
- 717
+ 709
+ 710
35
@@ -24664,23 +24443,23 @@
35
- 52896
- 52897
+ 47844
+ 47845
35
- 79931
- 79932
+ 74862
+ 74863
35
- 91328
- 91329
+ 86276
+ 86277
35
- 99658
- 99659
+ 94606
+ 94607
35
@@ -24691,15 +24470,15 @@
varspecifiers
- 2347848
+ 2310104
var_id
- 1255071
+ 1234894
spec_id
- 3763
+ 3702
@@ -24713,22 +24492,22 @@
1
2
- 735731
+ 723903
2
3
- 203219
+ 199952
3
4
- 58802
+ 57856
4
5
- 257317
+ 253181
@@ -24744,42 +24523,42 @@
112
113
- 470
+ 462
315
316
- 470
+ 462
414
415
- 470
+ 462
560
561
- 470
+ 462
692
693
- 470
+ 462
700
701
- 470
+ 462
732
733
- 470
+ 462
1466
1467
- 470
+ 462
@@ -24789,19 +24568,19 @@
attributes
- 696354
+ 695736
id
- 696354
+ 695736
kind
- 314
+ 313
name
- 1674
+ 1673
name_space
@@ -24809,7 +24588,7 @@
location
- 483847
+ 483417
@@ -24823,7 +24602,7 @@
1
2
- 696354
+ 695736
@@ -24839,7 +24618,7 @@
1
2
- 696354
+ 695736
@@ -24855,7 +24634,7 @@
1
2
- 696354
+ 695736
@@ -24871,7 +24650,7 @@
1
2
- 696354
+ 695736
@@ -25067,7 +24846,7 @@
1
2
- 1465
+ 1464
2
@@ -25088,7 +24867,7 @@
1
2
- 1674
+ 1673
@@ -25104,7 +24883,7 @@
1
2
- 314
+ 313
2
@@ -25264,17 +25043,17 @@
1
2
- 442497
+ 442104
2
9
- 36848
+ 36815
9
201
- 4501
+ 4497
@@ -25290,7 +25069,7 @@
1
2
- 483847
+ 483417
@@ -25306,12 +25085,12 @@
1
2
- 479555
+ 479129
2
3
- 4292
+ 4288
@@ -25327,7 +25106,7 @@
1
2
- 483847
+ 483417
@@ -25337,27 +25116,27 @@
attribute_args
- 352341
+ 348066
id
- 352341
+ 348066
kind
- 1411
+ 1388
attribute
- 270489
+ 267529
index
- 1411
+ 1388
location
- 329291
+ 324923
@@ -25371,7 +25150,7 @@
1
2
- 352341
+ 348066
@@ -25387,7 +25166,7 @@
1
2
- 352341
+ 348066
@@ -25403,7 +25182,7 @@
1
2
- 352341
+ 348066
@@ -25419,7 +25198,7 @@
1
2
- 352341
+ 348066
@@ -25435,17 +25214,17 @@
1
2
- 470
+ 462
54
55
- 470
+ 462
- 694
- 695
- 470
+ 697
+ 698
+ 462
@@ -25461,17 +25240,17 @@
1
2
- 470
+ 462
54
55
- 470
+ 462
- 542
- 543
- 470
+ 545
+ 546
+ 462
@@ -25487,12 +25266,12 @@
1
2
- 940
+ 925
3
4
- 470
+ 462
@@ -25508,17 +25287,17 @@
1
2
- 470
+ 462
54
55
- 470
+ 462
- 672
- 673
- 470
+ 674
+ 675
+ 462
@@ -25534,17 +25313,17 @@
1
2
- 204631
+ 202730
2
3
- 49864
+ 49062
3
4
- 15994
+ 15737
@@ -25560,12 +25339,12 @@
1
2
- 260140
+ 257346
2
3
- 10349
+ 10182
@@ -25581,17 +25360,17 @@
1
2
- 204631
+ 202730
2
3
- 49864
+ 49062
3
4
- 15994
+ 15737
@@ -25607,17 +25386,17 @@
1
2
- 204631
+ 202730
2
3
- 49864
+ 49062
3
4
- 15994
+ 15737
@@ -25633,17 +25412,17 @@
34
35
- 470
+ 462
140
141
- 470
+ 462
- 575
- 576
- 470
+ 578
+ 579
+ 462
@@ -25659,12 +25438,12 @@
1
2
- 940
+ 925
3
4
- 470
+ 462
@@ -25680,17 +25459,17 @@
34
35
- 470
+ 462
140
141
- 470
+ 462
- 575
- 576
- 470
+ 578
+ 579
+ 462
@@ -25706,17 +25485,17 @@
34
35
- 470
+ 462
140
141
- 470
+ 462
- 526
- 527
- 470
+ 528
+ 529
+ 462
@@ -25732,12 +25511,12 @@
1
2
- 315179
+ 311037
2
- 16
- 14112
+ 17
+ 13885
@@ -25753,12 +25532,12 @@
1
2
- 316590
+ 312426
2
3
- 12701
+ 12497
@@ -25774,12 +25553,12 @@
1
2
- 315179
+ 311037
2
- 16
- 14112
+ 17
+ 13885
@@ -25795,7 +25574,7 @@
1
2
- 329291
+ 324923
@@ -25805,15 +25584,15 @@
attribute_arg_value
- 351871
+ 24994
arg
- 351871
+ 24994
value
- 34810
+ 15737
@@ -25827,7 +25606,7 @@
1
2
- 351871
+ 24994
@@ -25843,22 +25622,12 @@
1
2
- 16934
+ 14348
2
- 3
- 12230
-
-
- 3
- 14
- 2822
-
-
- 15
- 247
- 2822
+ 16
+ 1388
@@ -25868,15 +25637,15 @@
attribute_arg_type
- 470
+ 462
arg
- 470
+ 462
type_id
- 470
+ 462
@@ -25890,7 +25659,7 @@
1
2
- 470
+ 462
@@ -25906,7 +25675,55 @@
1
2
- 470
+ 462
+
+
+
+
+
+
+
+
+ attribute_arg_constant
+ 367506
+
+
+ arg
+ 367506
+
+
+ constant
+ 367506
+
+
+
+
+ arg
+ constant
+
+
+ 12
+
+
+ 1
+ 2
+ 367506
+
+
+
+
+
+
+ constant
+ arg
+
+
+ 12
+
+
+ 1
+ 2
+ 367506
@@ -25969,15 +25786,15 @@
typeattributes
- 62496
+ 62440
type_id
- 62077
+ 62022
spec_id
- 62496
+ 62440
@@ -25991,7 +25808,7 @@
1
2
- 61658
+ 61603
2
@@ -26012,7 +25829,7 @@
1
2
- 62496
+ 62440
@@ -26022,15 +25839,15 @@
funcattributes
- 635532
+ 629948
func_id
- 447366
+ 592296
spec_id
- 635532
+ 629948
@@ -26044,22 +25861,12 @@
1
2
- 341522
+ 558408
2
- 3
- 64917
-
-
- 3
- 6
- 39985
-
-
- 6
7
- 940
+ 33887
@@ -26075,7 +25882,7 @@
1
2
- 635532
+ 629948
@@ -26143,15 +25950,15 @@
stmtattributes
- 1006
+ 1002
stmt_id
- 1006
+ 1002
spec_id
- 1006
+ 1002
@@ -26165,7 +25972,7 @@
1
2
- 1006
+ 1002
@@ -26181,7 +25988,7 @@
1
2
- 1006
+ 1002
@@ -26191,15 +25998,15 @@
unspecifiedtype
- 10352924
+ 10137428
type_id
- 10352924
+ 10137428
unspecified_type_id
- 6956047
+ 6822468
@@ -26213,7 +26020,7 @@
1
2
- 10352924
+ 10137428
@@ -26229,17 +26036,17 @@
1
2
- 4675939
+ 4591048
2
3
- 2037843
+ 1996752
3
147
- 242264
+ 234666
@@ -26249,19 +26056,19 @@
member
- 5134480
+ 4925926
parent
- 689567
+ 618819
index
- 8808
+ 8821
child
- 5070710
+ 4862061
@@ -26275,42 +26082,42 @@
1
3
- 18884
+ 18912
3
4
- 390691
+ 320347
4
5
- 39072
+ 38283
5
7
- 53059
+ 53138
7
10
- 52848
+ 52926
10
- 16
- 57569
+ 15
+ 50315
- 16
- 30
- 52954
+ 15
+ 24
+ 49609
- 30
+ 24
251
- 24486
+ 35284
@@ -26326,42 +26133,42 @@
1
3
- 18884
+ 18912
3
4
- 390656
+ 320312
4
5
- 39107
+ 38318
5
7
- 53165
+ 53244
7
10
- 53165
+ 53244
10
- 16
- 57323
+ 15
+ 49998
- 16
- 29
- 52742
+ 15
+ 24
+ 49962
- 29
+ 24
253
- 24521
+ 34825
@@ -26377,62 +26184,62 @@
1
2
- 1409
+ 1411
2
3
- 810
+ 811
3
4
- 951
+ 952
5
22
- 669
+ 670
22
42
- 669
+ 670
42
56
- 669
+ 670
56
100
- 669
+ 670
104
164
- 669
+ 670
181
299
- 669
+ 670
300
727
- 669
+ 670
845
4002
- 669
+ 670
4606
- 19241
- 281
+ 17207
+ 282
@@ -26448,61 +26255,61 @@
1
2
- 810
+ 811
2
3
- 880
+ 882
3
4
- 1162
+ 1164
4
15
- 669
+ 670
16
35
- 739
+ 740
36
55
- 669
+ 670
57
93
- 739
+ 740
97
135
- 669
+ 670
140
256
- 669
+ 670
268
612
- 669
+ 670
619
2611
- 669
+ 670
2770
- 19253
+ 17219
458
@@ -26519,7 +26326,7 @@
1
2
- 5070710
+ 4862061
@@ -26535,12 +26342,12 @@
1
2
- 5008419
+ 4799678
2
8
- 62290
+ 62382
@@ -26550,15 +26357,15 @@
enclosingfunction
- 121743
+ 121348
child
- 121743
+ 121348
parent
- 69504
+ 69279
@@ -26572,7 +26379,7 @@
1
2
- 121743
+ 121348
@@ -26588,22 +26395,22 @@
1
2
- 36695
+ 36576
2
3
- 21591
+ 21521
3
4
- 6106
+ 6086
4
45
- 5111
+ 5095
@@ -26613,15 +26420,15 @@
derivations
- 402388
+ 368264
derivation
- 402388
+ 368264
sub
- 381883
+ 347728
index
@@ -26629,11 +26436,11 @@
super
- 206461
+ 203873
location
- 38156
+ 38213
@@ -26647,7 +26454,7 @@
1
2
- 402388
+ 368264
@@ -26663,7 +26470,7 @@
1
2
- 402388
+ 368264
@@ -26679,7 +26486,7 @@
1
2
- 402388
+ 368264
@@ -26695,7 +26502,7 @@
1
2
- 402388
+ 368264
@@ -26711,12 +26518,12 @@
1
2
- 366733
+ 332556
2
7
- 15149
+ 15172
@@ -26732,12 +26539,12 @@
1
2
- 366733
+ 332556
2
7
- 15149
+ 15172
@@ -26753,12 +26560,12 @@
1
2
- 366733
+ 332556
2
7
- 15149
+ 15172
@@ -26774,12 +26581,12 @@
1
2
- 366733
+ 332556
2
7
- 15149
+ 15172
@@ -26808,8 +26615,8 @@
35
- 10839
- 10840
+ 9855
+ 9856
35
@@ -26839,8 +26646,8 @@
35
- 10839
- 10840
+ 9855
+ 9856
35
@@ -26875,8 +26682,8 @@
35
- 5505
- 5506
+ 5423
+ 5424
35
@@ -26924,12 +26731,12 @@
1
2
- 199133
+ 196534
2
- 1225
- 7328
+ 1216
+ 7339
@@ -26945,12 +26752,12 @@
1
2
- 199133
+ 196534
2
- 1225
- 7328
+ 1216
+ 7339
@@ -26966,7 +26773,7 @@
1
2
- 206003
+ 203415
2
@@ -26987,12 +26794,12 @@
1
2
- 202867
+ 200274
2
108
- 3593
+ 3599
@@ -27008,27 +26815,27 @@
1
2
- 28326
+ 28827
2
5
- 3135
+ 3140
5
16
- 2994
+ 2928
17
- 133
- 2994
+ 178
+ 2928
- 142
+ 192
474
- 704
+ 388
@@ -27044,27 +26851,27 @@
1
2
- 28326
+ 28827
2
5
- 3135
+ 3140
5
16
- 2994
+ 2928
17
- 133
- 2994
+ 178
+ 2928
- 142
+ 192
474
- 704
+ 388
@@ -27080,7 +26887,7 @@
1
2
- 38156
+ 38213
@@ -27096,22 +26903,22 @@
1
2
- 30757
+ 31120
2
5
- 3417
+ 3210
5
- 55
- 2889
+ 63
+ 2893
- 60
- 420
- 1092
+ 63
+ 415
+ 987
@@ -27121,15 +26928,15 @@
derspecifiers
- 404291
+ 370169
der_id
- 402001
+ 367876
spec_id
- 140
+ 141
@@ -27143,12 +26950,12 @@
1
2
- 399710
+ 365582
2
3
- 2290
+ 2293
@@ -27172,13 +26979,13 @@
35
- 1132
- 1133
+ 1127
+ 1128
35
- 10185
- 10186
+ 9206
+ 9207
35
@@ -27189,11 +26996,11 @@
direct_base_offsets
- 373110
+ 338942
der_id
- 373110
+ 338942
offset
@@ -27211,7 +27018,7 @@
1
2
- 373110
+ 338942
@@ -27250,8 +27057,8 @@
35
- 10484
- 10485
+ 9500
+ 9501
35
@@ -27262,19 +27069,19 @@
virtual_base_offsets
- 6661
+ 6639
sub
- 3677
+ 3665
super
- 508
+ 507
offset
- 254
+ 253
@@ -27288,12 +27095,12 @@
1
2
- 2891
+ 2881
2
4
- 323
+ 322
4
@@ -27303,7 +27110,7 @@
7
11
- 196
+ 195
@@ -27319,12 +27126,12 @@
1
2
- 3099
+ 3089
2
4
- 312
+ 311
4
@@ -27406,7 +27213,7 @@
1
2
- 289
+ 288
2
@@ -27553,23 +27360,23 @@
frienddecls
- 715075
+ 716133
id
- 715075
+ 716133
type_id
- 42384
+ 42447
decl_id
- 70182
+ 70286
location
- 6341
+ 6351
@@ -27583,7 +27390,7 @@
1
2
- 715075
+ 716133
@@ -27599,7 +27406,7 @@
1
2
- 715075
+ 716133
@@ -27615,7 +27422,7 @@
1
2
- 715075
+ 716133
@@ -27631,47 +27438,47 @@
1
2
- 6200
+ 6210
2
3
- 13212
+ 13231
3
6
- 2959
+ 2963
6
10
- 3206
+ 3210
10
17
- 3276
+ 3281
17
24
- 3347
+ 3352
25
36
- 3311
+ 3316
37
55
- 3241
+ 3246
55
103
- 3628
+ 3634
@@ -27687,47 +27494,47 @@
1
2
- 6200
+ 6210
2
3
- 13212
+ 13231
3
6
- 2959
+ 2963
6
10
- 3206
+ 3210
10
17
- 3276
+ 3281
17
24
- 3347
+ 3352
25
36
- 3311
+ 3316
37
55
- 3241
+ 3246
55
103
- 3628
+ 3634
@@ -27743,12 +27550,12 @@
1
2
- 40939
+ 41000
2
13
- 1444
+ 1446
@@ -27764,37 +27571,37 @@
1
2
- 40481
+ 40541
2
3
- 5883
+ 5892
3
8
- 6024
+ 6033
8
15
- 5425
+ 5433
15
32
- 5284
+ 5292
32
71
- 5284
+ 5292
72
160
- 1796
+ 1799
@@ -27810,37 +27617,37 @@
1
2
- 40481
+ 40541
2
3
- 5883
+ 5892
3
8
- 6024
+ 6033
8
15
- 5425
+ 5433
15
32
- 5284
+ 5292
32
71
- 5284
+ 5292
72
160
- 1796
+ 1799
@@ -27856,12 +27663,12 @@
1
2
- 69513
+ 69616
2
5
- 669
+ 670
@@ -27877,12 +27684,12 @@
1
2
- 5954
+ 5963
2
20106
- 387
+ 388
@@ -27898,12 +27705,12 @@
1
2
- 6200
+ 6210
2
1105
- 140
+ 141
@@ -27919,7 +27726,7 @@
1
2
- 5989
+ 5998
2
@@ -27934,19 +27741,19 @@
comments
- 8781270
+ 8773472
id
- 8781270
+ 8773472
contents
- 3342962
+ 3339994
location
- 8781270
+ 8773472
@@ -27960,7 +27767,7 @@
1
2
- 8781270
+ 8773472
@@ -27976,7 +27783,7 @@
1
2
- 8781270
+ 8773472
@@ -27992,17 +27799,17 @@
1
2
- 3058223
+ 3055507
2
7
- 251135
+ 250912
7
32784
- 33603
+ 33573
@@ -28018,17 +27825,17 @@
1
2
- 3058223
+ 3055507
2
7
- 251135
+ 250912
7
32784
- 33603
+ 33573
@@ -28044,7 +27851,7 @@
1
2
- 8781270
+ 8773472
@@ -28060,7 +27867,7 @@
1
2
- 8781270
+ 8773472
@@ -28070,15 +27877,15 @@
commentbinding
- 3145674
+ 3095104
id
- 2490384
+ 2450349
element
- 3068526
+ 3019196
@@ -28092,12 +27899,12 @@
1
2
- 2408061
+ 2369349
2
97
- 82322
+ 80999
@@ -28113,12 +27920,12 @@
1
2
- 2991378
+ 2943288
2
3
- 77148
+ 75908
@@ -28128,15 +27935,15 @@
exprconv
- 7003750
+ 7021832
converted
- 7003750
+ 7021832
conversion
- 7003750
+ 7021832
@@ -28150,7 +27957,7 @@
1
2
- 7003750
+ 7021832
@@ -28166,7 +27973,7 @@
1
2
- 7003750
+ 7021832
@@ -28176,30 +27983,30 @@
compgenerated
- 8494343
+ 8328197
id
- 8494343
+ 8328197
synthetic_destructor_call
- 133110
+ 144327
element
- 103484
+ 111770
i
- 306
+ 336
destructor_call
- 117766
+ 129098
@@ -28213,17 +28020,17 @@
1
2
- 85546
+ 92066
2
3
- 11832
+ 12991
3
18
- 6105
+ 6713
@@ -28239,17 +28046,17 @@
1
2
- 85546
+ 92066
2
3
- 11832
+ 12991
3
18
- 6105
+ 6713
@@ -28265,67 +28072,67 @@
1
2
- 18
+ 19
2
3
- 54
+ 59
3
4
- 18
+ 19
4
5
- 54
+ 59
6
7
- 18
+ 19
11
12
- 18
+ 19
20
21
- 18
+ 19
34
35
- 18
+ 19
65
66
- 18
+ 19
152
153
- 18
+ 19
339
340
- 18
+ 19
- 996
- 997
- 18
+ 995
+ 996
+ 19
- 5746
- 5747
- 18
+ 5644
+ 5645
+ 19
@@ -28341,67 +28148,67 @@
1
2
- 18
+ 19
2
3
- 54
+ 59
3
4
- 18
+ 19
4
5
- 54
+ 59
6
7
- 18
+ 19
11
12
- 18
+ 19
20
21
- 18
+ 19
34
35
- 18
+ 19
65
66
- 18
+ 19
151
152
- 18
+ 19
338
339
- 18
+ 19
- 995
- 996
- 18
+ 994
+ 995
+ 19
- 4897
- 4898
- 18
+ 4878
+ 4879
+ 19
@@ -28417,12 +28224,12 @@
1
2
- 115749
+ 127059
2
26
- 2017
+ 2039
@@ -28438,7 +28245,7 @@
1
2
- 117766
+ 129098
@@ -28448,15 +28255,15 @@
namespaces
- 12701
+ 12497
id
- 12701
+ 12497
name
- 10349
+ 10182
@@ -28470,7 +28277,7 @@
1
2
- 12701
+ 12497
@@ -28486,17 +28293,17 @@
1
2
- 8937
+ 8794
2
3
- 470
+ 462
3
4
- 940
+ 925
@@ -28506,26 +28313,26 @@
namespace_inline
- 1411
+ 1388
id
- 1411
+ 1388
namespacembrs
- 2463100
+ 2389715
parentid
- 10819
+ 10645
memberid
- 2463100
+ 2389715
@@ -28539,57 +28346,57 @@
1
2
- 1881
+ 1851
2
3
- 940
+ 925
3
4
- 470
+ 462
4
5
- 940
+ 925
5
7
- 940
+ 925
7
8
- 940
+ 925
8
12
- 940
+ 925
17
30
- 940
+ 925
43
47
- 940
+ 925
52
143
- 940
+ 925
253
- 4592
- 940
+ 4519
+ 925
@@ -28605,7 +28412,7 @@
1
2
- 2463100
+ 2389715
@@ -28615,19 +28422,19 @@
exprparents
- 14152882
+ 14182785
expr_id
- 14152882
+ 14182785
child_index
- 14602
+ 14633
parent_id
- 9417999
+ 9437898
@@ -28641,7 +28448,7 @@
1
2
- 14152882
+ 14182785
@@ -28657,7 +28464,7 @@
1
2
- 14152882
+ 14182785
@@ -28673,37 +28480,37 @@
1
2
- 2809
+ 2815
2
3
- 1107
+ 1109
3
4
- 266
+ 267
4
5
- 6542
+ 6556
5
8
- 1210
+ 1212
8
11
- 1189
+ 1192
11
53
- 1107
+ 1109
56
@@ -28724,37 +28531,37 @@
1
2
- 2809
+ 2815
2
3
- 1107
+ 1109
3
4
- 266
+ 267
4
5
- 6542
+ 6556
5
8
- 1210
+ 1212
8
11
- 1189
+ 1192
11
53
- 1107
+ 1109
56
@@ -28775,17 +28582,17 @@
1
2
- 5388939
+ 5400325
2
3
- 3692597
+ 3700399
3
712
- 336462
+ 337173
@@ -28801,17 +28608,17 @@
1
2
- 5388939
+ 5400325
2
3
- 3692597
+ 3700399
3
712
- 336462
+ 337173
@@ -28821,22 +28628,22 @@
expr_isload
- 4981779
+ 4982130
expr_id
- 4981779
+ 4982130
conversionkinds
- 4220621
+ 4220624
expr_id
- 4220621
+ 4220624
kind
@@ -28854,7 +28661,7 @@
1
2
- 4220621
+ 4220624
@@ -28883,8 +28690,8 @@
1
- 26289
- 26290
+ 26287
+ 26288
1
@@ -28893,8 +28700,8 @@
1
- 4131029
- 4131030
+ 4131034
+ 4131035
1
@@ -28905,15 +28712,15 @@
iscall
- 3078157
+ 2951156
caller
- 3078157
+ 2951156
kind
- 54
+ 59
@@ -28927,7 +28734,7 @@
1
2
- 3078157
+ 2951156
@@ -28941,19 +28748,19 @@
12
- 1378
- 1379
- 18
+ 1318
+ 1319
+ 19
- 2512
- 2513
- 18
+ 2470
+ 2471
+ 19
- 167025
- 167026
- 18
+ 145234
+ 145235
+ 19
@@ -28963,15 +28770,15 @@
numtemplatearguments
- 543303
+ 396244
expr_id
- 543303
+ 396244
num
- 72
+ 317
@@ -28985,7 +28792,7 @@
1
2
- 543303
+ 396244
@@ -28999,24 +28806,39 @@
12
- 26
- 27
- 18
+ 1
+ 2
+ 105
- 28
- 29
- 18
+ 4
+ 5
+ 35
- 220
- 221
- 18
+ 20
+ 21
+ 35
- 29893
- 29894
- 18
+ 101
+ 102
+ 35
+
+
+ 179
+ 180
+ 35
+
+
+ 227
+ 228
+ 35
+
+
+ 10696
+ 10697
+ 35
@@ -29026,15 +28848,15 @@
specialnamequalifyingelements
- 470
+ 462
id
- 470
+ 462
name
- 470
+ 462
@@ -29048,7 +28870,7 @@
1
2
- 470
+ 462
@@ -29064,7 +28886,7 @@
1
2
- 470
+ 462
@@ -29074,23 +28896,23 @@
namequalifiers
- 1618961
+ 1536058
id
- 1618961
+ 1536058
qualifiableelement
- 1618961
+ 1536058
qualifyingelement
- 79675
+ 83412
location
- 282719
+ 306003
@@ -29104,7 +28926,7 @@
1
2
- 1618961
+ 1536058
@@ -29120,7 +28942,7 @@
1
2
- 1618961
+ 1536058
@@ -29136,7 +28958,7 @@
1
2
- 1618961
+ 1536058
@@ -29152,7 +28974,7 @@
1
2
- 1618961
+ 1536058
@@ -29168,7 +28990,7 @@
1
2
- 1618961
+ 1536058
@@ -29184,7 +29006,7 @@
1
2
- 1618961
+ 1536058
@@ -29200,27 +29022,27 @@
1
2
- 45546
+ 46617
2
3
- 17163
+ 20615
3
4
- 6195
+ 5049
4
- 8
- 6087
+ 7
+ 6396
- 8
- 31227
- 4682
+ 7
+ 21072
+ 4733
@@ -29236,27 +29058,27 @@
1
2
- 45546
+ 46617
2
3
- 17163
+ 20615
3
4
- 6195
+ 5049
4
- 8
- 6087
+ 7
+ 6396
- 8
- 31227
- 4682
+ 7
+ 21072
+ 4733
@@ -29272,27 +29094,27 @@
1
2
- 49725
+ 50954
2
3
- 15992
+ 19526
3
4
- 6411
+ 4851
4
- 9
- 6123
+ 8
+ 6257
- 9
+ 8
7095
- 1422
+ 1821
@@ -29308,32 +29130,32 @@
1
2
- 91742
+ 98304
2
3
- 25646
+ 27408
3
4
- 42179
+ 45884
4
6
- 12895
+ 14080
6
7
- 89869
+ 98878
7
- 2135
- 20387
+ 782
+ 21447
@@ -29349,32 +29171,32 @@
1
2
- 91742
+ 98304
2
3
- 25646
+ 27408
3
4
- 42179
+ 45884
4
6
- 12895
+ 14080
6
7
- 89869
+ 98878
7
- 2135
- 20387
+ 782
+ 21447
@@ -29390,22 +29212,22 @@
1
2
- 125168
+ 134426
2
3
- 52354
+ 56796
3
4
- 96622
+ 105968
4
- 152
- 8572
+ 143
+ 8812
@@ -29415,15 +29237,15 @@
varbind
- 6006364
+ 6019055
expr
- 6006364
+ 6019055
var
- 765629
+ 767246
@@ -29437,7 +29259,7 @@
1
2
- 6006364
+ 6019055
@@ -29453,52 +29275,52 @@
1
2
- 125745
+ 126011
2
3
- 137353
+ 137644
3
4
- 105891
+ 106115
4
5
- 84889
+ 85069
5
6
- 61057
+ 61186
6
7
- 47931
+ 48032
7
9
- 59396
+ 59521
9
13
- 59047
+ 59172
13
28
- 58657
+ 58781
28
5137
- 25657
+ 25711
@@ -29508,15 +29330,15 @@
funbind
- 3080553
+ 2954265
expr
- 3076933
+ 2951453
fun
- 514055
+ 533842
@@ -29530,12 +29352,12 @@
1
2
- 3073313
+ 2948641
2
3
- 3619
+ 2812
@@ -29551,32 +29373,32 @@
1
2
- 306582
+ 329787
2
3
- 78775
+ 82184
3
4
- 37226
+ 31883
4
7
- 43475
+ 48043
7
- 38
- 38703
+ 158
+ 40042
- 38
+ 159
4943
- 9293
+ 1901
@@ -29586,11 +29408,11 @@
expr_allocator
- 46541
+ 46610
expr
- 46541
+ 46610
func
@@ -29612,7 +29434,7 @@
1
2
- 46541
+ 46610
@@ -29628,7 +29450,7 @@
1
2
- 46541
+ 46610
@@ -29712,11 +29534,11 @@
expr_deallocator
- 55314
+ 55396
expr
- 55314
+ 55396
func
@@ -29738,7 +29560,7 @@
1
2
- 55314
+ 55396
@@ -29754,7 +29576,7 @@
1
2
- 55314
+ 55396
@@ -29848,26 +29670,26 @@
expr_cond_two_operand
- 484
+ 480
cond
- 484
+ 480
expr_cond_guard
- 654502
+ 656192
cond
- 654502
+ 656192
guard
- 654502
+ 656192
@@ -29881,7 +29703,7 @@
1
2
- 654502
+ 656192
@@ -29897,7 +29719,7 @@
1
2
- 654502
+ 656192
@@ -29907,15 +29729,15 @@
expr_cond_true
- 654499
+ 656189
cond
- 654499
+ 656189
true
- 654499
+ 656189
@@ -29929,7 +29751,7 @@
1
2
- 654499
+ 656189
@@ -29945,7 +29767,7 @@
1
2
- 654499
+ 656189
@@ -29955,15 +29777,15 @@
expr_cond_false
- 654502
+ 656192
cond
- 654502
+ 656192
false
- 654502
+ 656192
@@ -29977,7 +29799,7 @@
1
2
- 654502
+ 656192
@@ -29993,7 +29815,7 @@
1
2
- 654502
+ 656192
@@ -30003,15 +29825,15 @@
values
- 10646146
+ 10759270
id
- 10646146
+ 10759270
str
- 86639
+ 87848
@@ -30025,7 +29847,7 @@
1
2
- 10646146
+ 10759270
@@ -30041,27 +29863,27 @@
1
2
- 58708
+ 59389
2
3
- 12259
+ 12364
3
6
- 6747
+ 6924
6
- 62
- 6513
+ 56
+ 6612
- 62
- 451065
- 2410
+ 57
+ 452050
+ 2556
@@ -30071,15 +29893,15 @@
valuetext
- 4756729
+ 4757155
id
- 4756729
+ 4757155
text
- 703924
+ 703935
@@ -30093,7 +29915,7 @@
1
2
- 4756729
+ 4757155
@@ -30109,22 +29931,22 @@
1
2
- 527529
+ 527534
2
3
- 102489
+ 102488
3
7
- 56759
+ 56764
7
425881
- 17147
+ 17149
@@ -30134,15 +29956,15 @@
valuebind
- 11083050
+ 11192950
val
- 10646146
+ 10759270
expr
- 11083050
+ 11192950
@@ -30156,12 +29978,12 @@
1
2
- 10232022
+ 10348201
2
7
- 414124
+ 411068
@@ -30177,7 +29999,7 @@
1
2
- 11083050
+ 11192950
@@ -30187,19 +30009,19 @@
fieldoffsets
- 1050083
+ 1052962
id
- 1050083
+ 1052962
byteoffset
- 22593
+ 22655
bitoffset
- 318
+ 319
@@ -30213,7 +30035,7 @@
1
2
- 1050083
+ 1052962
@@ -30229,7 +30051,7 @@
1
2
- 1050083
+ 1052962
@@ -30245,37 +30067,37 @@
1
2
- 12967
+ 13002
2
3
- 1710
+ 1715
3
5
- 1789
+ 1794
5
12
- 1909
+ 1914
12
35
- 1710
+ 1715
35
205
- 1710
+ 1715
244
5638
- 795
+ 797
@@ -30291,12 +30113,12 @@
1
2
- 21917
+ 21977
2
9
- 676
+ 678
@@ -30388,19 +30210,19 @@
bitfield
- 20936
+ 20918
id
- 20936
+ 20918
bits
- 2617
+ 2614
declared_bits
- 2617
+ 2614
@@ -30414,7 +30236,7 @@
1
2
- 20936
+ 20918
@@ -30430,7 +30252,7 @@
1
2
- 20936
+ 20918
@@ -30451,7 +30273,7 @@
2
3
- 628
+ 627
3
@@ -30497,7 +30319,7 @@
1
2
- 2617
+ 2614
@@ -30518,7 +30340,7 @@
2
3
- 628
+ 627
3
@@ -30564,7 +30386,7 @@
1
2
- 2617
+ 2614
@@ -30574,23 +30396,23 @@
initialisers
- 1731824
+ 1733596
init
- 1731824
+ 1733596
var
- 717748
+ 722038
expr
- 1731824
+ 1733596
location
- 390436
+ 390813
@@ -30604,7 +30426,7 @@
1
2
- 1731824
+ 1733596
@@ -30620,7 +30442,7 @@
1
2
- 1731824
+ 1733596
@@ -30636,7 +30458,7 @@
1
2
- 1731824
+ 1733596
@@ -30652,22 +30474,17 @@
1
2
- 629392
+ 633767
2
16
- 31625
+ 31559
16
25
- 56686
-
-
- 25
- 112
- 44
+ 56711
@@ -30683,22 +30500,17 @@
1
2
- 629392
+ 633767
2
16
- 31625
+ 31559
16
25
- 56686
-
-
- 25
- 112
- 44
+ 56711
@@ -30714,91 +30526,91 @@
1
2
- 717666
-
-
- 2
- 4
- 81
-
-
-
-
-
-
- expr
- init
-
-
- 12
-
-
- 1
- 2
- 1731824
-
-
-
-
-
-
- expr
- var
-
-
- 12
-
-
- 1
- 2
- 1731824
-
-
-
-
-
-
- expr
- location
-
-
- 12
-
-
- 1
- 2
- 1731824
-
-
-
-
-
-
- location
- init
-
-
- 12
-
-
- 1
- 2
- 317956
+ 722032
2
3
- 23835
+ 6
+
+
+
+
+
+
+ expr
+ init
+
+
+ 12
+
+
+ 1
+ 2
+ 1733596
+
+
+
+
+
+
+ expr
+ var
+
+
+ 12
+
+
+ 1
+ 2
+ 1733596
+
+
+
+
+
+
+ expr
+ location
+
+
+ 12
+
+
+ 1
+ 2
+ 1733596
+
+
+
+
+
+
+ location
+ init
+
+
+ 12
+
+
+ 1
+ 2
+ 318365
+
+
+ 2
+ 3
+ 23842
3
15
- 30789
+ 30753
15
- 111459
- 17855
+ 111523
+ 17850
@@ -30814,17 +30626,17 @@
1
2
- 340955
+ 341138
2
4
- 35579
+ 35619
4
- 12738
- 13901
+ 12802
+ 14055
@@ -30840,22 +30652,22 @@
1
2
- 317956
+ 318365
2
3
- 23835
+ 23842
3
15
- 30789
+ 30753
15
- 111459
- 17855
+ 111523
+ 17850
@@ -30865,26 +30677,26 @@
braced_initialisers
- 41525
+ 41632
init
- 41525
+ 41632
expr_ancestor
- 121674
+ 133396
exp
- 121674
+ 133396
ancestor
- 84880
+ 92957
@@ -30898,7 +30710,7 @@
1
2
- 121674
+ 133396
@@ -30914,22 +30726,22 @@
1
2
- 61377
+ 67133
2
3
- 16785
+ 18437
3
8
- 6483
+ 7129
8
18
- 234
+ 257
@@ -30939,19 +30751,19 @@
exprs
- 18300140
+ 18356790
id
- 18300140
+ 18356790
kind
- 3382
+ 3387
location
- 3561673
+ 3591749
@@ -30965,7 +30777,7 @@
1
2
- 18300140
+ 18356790
@@ -30981,7 +30793,7 @@
1
2
- 18300140
+ 18356790
@@ -30996,68 +30808,68 @@
1
- 5
- 281
+ 3
+ 246
- 5
+ 4
14
- 211
+ 282
14
38
- 281
+ 282
42
- 66
- 281
+ 83
+ 282
- 82
- 135
- 281
+ 85
+ 142
+ 282
- 141
- 334
- 281
+ 145
+ 339
+ 282
- 338
- 509
- 281
+ 364
+ 564
+ 282
- 563
+ 653
830
- 281
+ 282
- 831
- 1183
- 281
+ 973
+ 1185
+ 282
- 1184
- 2071
- 281
+ 1329
+ 2628
+ 282
- 2627
- 5700
- 281
+ 3015
+ 6254
+ 282
- 6591
- 63491
- 281
+ 6592
+ 78899
+ 282
- 78915
- 109590
- 70
+ 109462
+ 109463
+ 35
@@ -31073,7 +30885,7 @@
1
2
- 281
+ 282
2
@@ -31083,17 +30895,17 @@
3
6
- 281
+ 282
6
13
- 281
+ 282
14
26
- 281
+ 282
28
@@ -31103,37 +30915,37 @@
63
83
- 281
+ 282
91
183
- 281
+ 282
206
342
- 281
+ 282
353
448
- 281
+ 282
468
- 1018
- 281
+ 1019
+ 282
1051
- 14609
- 281
+ 14618
+ 282
- 16974
- 32757
- 140
+ 16977
+ 32762
+ 141
@@ -31149,32 +30961,32 @@
1
2
- 1936933
+ 1941352
2
3
- 816932
+ 837336
3
4
- 247260
+ 248367
4
8
- 280872
+ 284181
8
- 136
- 267131
+ 155
+ 269397
- 136
- 54140
- 12542
+ 155
+ 53476
+ 11114
@@ -31190,22 +31002,22 @@
1
2
- 2363808
+ 2391758
2
3
- 873691
+ 875231
3
6
- 307261
+ 307821
6
25
- 16911
+ 16936
@@ -31215,19 +31027,19 @@
expr_types
- 18357789
+ 18411724
id
- 18300140
+ 18356790
typeid
- 829282
+ 881136
value_category
- 54
+ 59
@@ -31241,12 +31053,12 @@
1
2
- 18242562
+ 18301855
2
- 5
- 57577
+ 3
+ 54934
@@ -31262,7 +31074,7 @@
1
2
- 18300140
+ 18356790
@@ -31278,42 +31090,42 @@
1
2
- 293470
+ 316816
2
3
- 160720
+ 172230
3
4
- 69824
+ 69450
4
5
- 60801
+ 68262
5
7
- 66996
+ 69728
7
12
- 65321
+ 69886
12
35
- 62638
+ 66916
35
- 78674
- 49509
+ 73116
+ 47845
@@ -31329,17 +31141,17 @@
1
2
- 716180
+ 758038
2
3
- 102314
+ 111869
3
4
- 10787
+ 11228
@@ -31353,19 +31165,19 @@
12
- 11828
- 11829
- 18
+ 7159
+ 7160
+ 19
- 253738
- 253739
- 18
+ 235315
+ 235316
+ 19
- 750551
- 750552
- 18
+ 684473
+ 684474
+ 19
@@ -31379,19 +31191,19 @@
12
- 1446
- 1447
- 18
+ 1406
+ 1407
+ 19
- 11978
- 11979
- 18
+ 11860
+ 11861
+ 19
- 39501
- 39502
- 18
+ 38011
+ 38012
+ 19
@@ -31401,15 +31213,15 @@
new_allocated_type
- 47598
+ 47669
expr
- 47598
+ 47669
type_id
- 28150
+ 28192
@@ -31423,7 +31235,7 @@
1
2
- 47598
+ 47669
@@ -31439,17 +31251,17 @@
1
2
- 11767
+ 11785
2
3
- 14903
+ 14925
3
19
- 1479
+ 1481
@@ -31459,15 +31271,15 @@
new_array_allocated_type
- 5099
+ 5104
expr
- 5099
+ 5104
type_id
- 2194
+ 2196
@@ -31481,7 +31293,7 @@
1
2
- 5099
+ 5104
@@ -31502,7 +31314,7 @@
2
3
- 1942
+ 1944
3
@@ -32069,15 +31881,15 @@
condition_decl_bind
- 38595
+ 42438
expr
- 38595
+ 42438
decl
- 38595
+ 42438
@@ -32091,7 +31903,7 @@
1
2
- 38595
+ 42438
@@ -32107,7 +31919,7 @@
1
2
- 38595
+ 42438
@@ -32117,15 +31929,15 @@
typeid_bind
- 36430
+ 36484
expr
- 36430
+ 36484
type_id
- 16383
+ 16407
@@ -32139,7 +31951,7 @@
1
2
- 36430
+ 36484
@@ -32155,12 +31967,12 @@
1
2
- 15960
+ 15983
3
328
- 422
+ 423
@@ -32170,15 +31982,15 @@
uuidof_bind
- 19993
+ 20022
expr
- 19993
+ 20022
type_id
- 19798
+ 19827
@@ -32192,7 +32004,7 @@
1
2
- 19993
+ 20022
@@ -32208,7 +32020,7 @@
1
2
- 19635
+ 19663
2
@@ -32223,15 +32035,15 @@
sizeof_bind
- 191854
+ 198889
expr
- 191854
+ 198889
type_id
- 8194
+ 8165
@@ -32245,7 +32057,7 @@
1
2
- 191854
+ 198889
@@ -32261,12 +32073,12 @@
1
2
- 2697
+ 2683
2
3
- 2330
+ 2329
3
@@ -32276,27 +32088,27 @@
4
5
- 750
+ 739
5
6
- 212
+ 211
6
9
- 723
+ 713
9
133
- 649
+ 654
164
18023
- 53
+ 58
@@ -32354,19 +32166,19 @@
lambdas
- 21639
+ 21291
expr
- 21639
+ 21291
default_capture
- 470
+ 462
has_explicit_return_type
- 470
+ 462
@@ -32380,7 +32192,7 @@
1
2
- 21639
+ 21291
@@ -32396,7 +32208,7 @@
1
2
- 21639
+ 21291
@@ -32412,7 +32224,7 @@
46
47
- 470
+ 462
@@ -32428,7 +32240,7 @@
1
2
- 470
+ 462
@@ -32444,7 +32256,7 @@
46
47
- 470
+ 462
@@ -32460,7 +32272,7 @@
1
2
- 470
+ 462
@@ -32470,35 +32282,35 @@
lambda_capture
- 28224
+ 27771
id
- 28224
+ 27771
lambda
- 20698
+ 20365
index
- 940
+ 925
field
- 28224
+ 27771
captured_by_reference
- 470
+ 462
is_implicit
- 470
+ 462
location
- 2822
+ 2777
@@ -32512,7 +32324,7 @@
1
2
- 28224
+ 27771
@@ -32528,7 +32340,7 @@
1
2
- 28224
+ 27771
@@ -32544,7 +32356,7 @@
1
2
- 28224
+ 27771
@@ -32560,7 +32372,7 @@
1
2
- 28224
+ 27771
@@ -32576,7 +32388,7 @@
1
2
- 28224
+ 27771
@@ -32592,7 +32404,7 @@
1
2
- 28224
+ 27771
@@ -32608,12 +32420,12 @@
1
2
- 13171
+ 12959
2
3
- 7526
+ 7405
@@ -32629,12 +32441,12 @@
1
2
- 13171
+ 12959
2
3
- 7526
+ 7405
@@ -32650,12 +32462,12 @@
1
2
- 13171
+ 12959
2
3
- 7526
+ 7405
@@ -32671,7 +32483,7 @@
1
2
- 20698
+ 20365
@@ -32687,7 +32499,7 @@
1
2
- 20698
+ 20365
@@ -32703,12 +32515,12 @@
1
2
- 13171
+ 12959
2
3
- 7526
+ 7405
@@ -32724,12 +32536,12 @@
16
17
- 470
+ 462
44
45
- 470
+ 462
@@ -32745,12 +32557,12 @@
16
17
- 470
+ 462
44
45
- 470
+ 462
@@ -32766,12 +32578,12 @@
16
17
- 470
+ 462
44
45
- 470
+ 462
@@ -32787,7 +32599,7 @@
1
2
- 940
+ 925
@@ -32803,7 +32615,7 @@
1
2
- 940
+ 925
@@ -32819,12 +32631,12 @@
2
3
- 470
+ 462
4
5
- 470
+ 462
@@ -32840,7 +32652,7 @@
1
2
- 28224
+ 27771
@@ -32856,7 +32668,7 @@
1
2
- 28224
+ 27771
@@ -32872,7 +32684,7 @@
1
2
- 28224
+ 27771
@@ -32888,7 +32700,7 @@
1
2
- 28224
+ 27771
@@ -32904,7 +32716,7 @@
1
2
- 28224
+ 27771
@@ -32920,7 +32732,7 @@
1
2
- 28224
+ 27771
@@ -32936,7 +32748,7 @@
60
61
- 470
+ 462
@@ -32952,7 +32764,7 @@
44
45
- 470
+ 462
@@ -32968,7 +32780,7 @@
2
3
- 470
+ 462
@@ -32984,7 +32796,7 @@
60
61
- 470
+ 462
@@ -33000,7 +32812,7 @@
1
2
- 470
+ 462
@@ -33016,7 +32828,7 @@
6
7
- 470
+ 462
@@ -33032,7 +32844,7 @@
60
61
- 470
+ 462
@@ -33048,7 +32860,7 @@
44
45
- 470
+ 462
@@ -33064,7 +32876,7 @@
2
3
- 470
+ 462
@@ -33080,7 +32892,7 @@
60
61
- 470
+ 462
@@ -33096,7 +32908,7 @@
1
2
- 470
+ 462
@@ -33112,7 +32924,7 @@
6
7
- 470
+ 462
@@ -33128,12 +32940,12 @@
8
9
- 1881
+ 1851
14
15
- 940
+ 925
@@ -33149,12 +32961,12 @@
8
9
- 1881
+ 1851
14
15
- 940
+ 925
@@ -33170,7 +32982,7 @@
1
2
- 2822
+ 2777
@@ -33186,12 +32998,12 @@
8
9
- 1881
+ 1851
14
15
- 940
+ 925
@@ -33207,7 +33019,7 @@
1
2
- 2822
+ 2777
@@ -33223,7 +33035,7 @@
1
2
- 2822
+ 2777
@@ -33349,19 +33161,19 @@
stmts
- 4660299
+ 4653233
id
- 4660299
+ 4653233
kind
- 1988
+ 1987
location
- 2286915
+ 2284884
@@ -33375,7 +33187,7 @@
1
2
- 4660299
+ 4653233
@@ -33391,7 +33203,7 @@
1
2
- 4660299
+ 4653233
@@ -33485,8 +33297,8 @@
104
- 8770
- 8771
+ 8756
+ 8757
104
@@ -33495,8 +33307,8 @@
104
- 13297
- 13298
+ 13283
+ 13284
104
@@ -33619,22 +33431,22 @@
1
2
- 1892154
+ 1890473
2
4
- 175972
+ 175816
4
12
- 176182
+ 176025
12
- 699
- 42606
+ 687
+ 42568
@@ -33650,12 +33462,12 @@
1
2
- 2229653
+ 2227673
2
8
- 57261
+ 57211
@@ -33713,15 +33525,15 @@
variable_vla
- 21
+ 22
var
- 21
+ 22
decl
- 21
+ 22
@@ -33735,7 +33547,7 @@
1
2
- 21
+ 22
@@ -33751,7 +33563,7 @@
1
2
- 21
+ 22
@@ -33761,15 +33573,15 @@
if_initialization
- 314
+ 313
if_stmt
- 314
+ 313
init_id
- 314
+ 313
@@ -33783,7 +33595,7 @@
1
2
- 314
+ 313
@@ -33799,7 +33611,7 @@
1
2
- 314
+ 313
@@ -33809,15 +33621,15 @@
if_then
- 723174
+ 724702
if_stmt
- 723174
+ 724702
then_id
- 723174
+ 724702
@@ -33831,7 +33643,7 @@
1
2
- 723174
+ 724702
@@ -33847,7 +33659,7 @@
1
2
- 723174
+ 724702
@@ -33857,15 +33669,15 @@
if_else
- 183972
+ 184361
if_stmt
- 183972
+ 184361
else_id
- 183972
+ 184361
@@ -33879,7 +33691,7 @@
1
2
- 183972
+ 184361
@@ -33895,7 +33707,7 @@
1
2
- 183972
+ 184361
@@ -33953,15 +33765,15 @@
constexpr_if_then
- 52551
+ 52504
constexpr_if_stmt
- 52551
+ 52504
then_id
- 52551
+ 52504
@@ -33975,7 +33787,7 @@
1
2
- 52551
+ 52504
@@ -33991,7 +33803,7 @@
1
2
- 52551
+ 52504
@@ -34001,15 +33813,15 @@
constexpr_if_else
- 30881
+ 30854
constexpr_if_stmt
- 30881
+ 30854
else_id
- 30881
+ 30854
@@ -34023,7 +33835,7 @@
1
2
- 30881
+ 30854
@@ -34039,7 +33851,7 @@
1
2
- 30881
+ 30854
@@ -34049,15 +33861,15 @@
while_body
- 30207
+ 30109
while_stmt
- 30207
+ 30109
body_id
- 30207
+ 30109
@@ -34071,7 +33883,7 @@
1
2
- 30207
+ 30109
@@ -34087,7 +33899,7 @@
1
2
- 30207
+ 30109
@@ -34097,15 +33909,15 @@
do_body
- 148604
+ 148628
do_stmt
- 148604
+ 148628
body_id
- 148604
+ 148628
@@ -34119,7 +33931,7 @@
1
2
- 148604
+ 148628
@@ -34135,7 +33947,7 @@
1
2
- 148604
+ 148628
@@ -34193,19 +34005,19 @@
switch_case
- 191408
+ 209699
switch_stmt
- 10301
+ 11228
index
- 4430
+ 4871
case_id
- 191408
+ 209699
@@ -34219,57 +34031,57 @@
2
3
- 54
+ 59
3
4
- 2269
+ 2495
4
5
- 1656
+ 1821
5
6
- 1008
+ 1089
6
- 7
- 756
+ 8
+ 1029
- 7
+ 8
9
- 720
+ 554
9
10
- 972
+ 1069
10
- 11
- 324
+ 12
+ 1029
- 11
- 14
- 864
+ 12
+ 25
+ 871
- 14
- 31
- 828
+ 30
+ 152
+ 851
- 36
+ 181
247
- 846
+ 356
@@ -34285,57 +34097,57 @@
2
3
- 54
+ 59
3
4
- 2269
+ 2495
4
5
- 1656
+ 1821
5
6
- 1008
+ 1089
6
- 7
- 756
+ 8
+ 1029
- 7
+ 8
9
- 720
+ 554
9
10
- 972
+ 1069
10
- 11
- 324
+ 12
+ 1029
- 11
- 14
- 864
+ 12
+ 25
+ 871
- 14
- 31
- 828
+ 30
+ 152
+ 851
- 36
+ 181
247
- 846
+ 356
@@ -34351,32 +34163,32 @@
14
15
- 1170
+ 1287
18
19
- 540
+ 594
32
33
- 1909
+ 2099
33
62
- 378
+ 415
66
- 296
- 342
+ 292
+ 376
- 351
- 573
- 90
+ 346
+ 568
+ 99
@@ -34392,32 +34204,32 @@
14
15
- 1170
+ 1287
18
19
- 540
+ 594
32
33
- 1909
+ 2099
33
62
- 378
+ 415
66
- 296
- 342
+ 292
+ 376
- 351
- 573
- 90
+ 346
+ 568
+ 99
@@ -34433,7 +34245,7 @@
1
2
- 191408
+ 209699
@@ -34449,7 +34261,7 @@
1
2
- 191408
+ 209699
@@ -34459,15 +34271,15 @@
switch_body
- 20901
+ 20747
switch_stmt
- 20901
+ 20747
body_id
- 20901
+ 20747
@@ -34481,7 +34293,7 @@
1
2
- 20901
+ 20747
@@ -34497,7 +34309,7 @@
1
2
- 20901
+ 20747
@@ -34507,15 +34319,15 @@
for_initialization
- 53202
+ 53314
for_stmt
- 53202
+ 53314
init_id
- 53202
+ 53314
@@ -34529,7 +34341,7 @@
1
2
- 53202
+ 53314
@@ -34545,7 +34357,7 @@
1
2
- 53202
+ 53314
@@ -34555,15 +34367,15 @@
for_condition
- 55458
+ 55575
for_stmt
- 55458
+ 55575
condition_id
- 55458
+ 55575
@@ -34577,7 +34389,7 @@
1
2
- 55458
+ 55575
@@ -34593,7 +34405,7 @@
1
2
- 55458
+ 55575
@@ -34603,15 +34415,15 @@
for_update
- 53304
+ 53417
for_stmt
- 53304
+ 53417
update_id
- 53304
+ 53417
@@ -34625,7 +34437,7 @@
1
2
- 53304
+ 53417
@@ -34641,7 +34453,7 @@
1
2
- 53304
+ 53417
@@ -34651,15 +34463,15 @@
for_body
- 61324
+ 61453
for_stmt
- 61324
+ 61453
body_id
- 61324
+ 61453
@@ -34673,7 +34485,7 @@
1
2
- 61324
+ 61453
@@ -34689,7 +34501,7 @@
1
2
- 61324
+ 61453
@@ -34699,19 +34511,19 @@
stmtparents
- 4052305
+ 4056811
id
- 4052305
+ 4056811
index
- 12209
+ 12223
parent
- 1719463
+ 1721378
@@ -34725,7 +34537,7 @@
1
2
- 4052305
+ 4056811
@@ -34741,7 +34553,7 @@
1
2
- 4052305
+ 4056811
@@ -34757,12 +34569,12 @@
1
2
- 4011
+ 4015
2
3
- 999
+ 1000
3
@@ -34772,37 +34584,37 @@
4
5
- 1552
+ 1554
7
8
- 1018
+ 1019
8
12
- 792
+ 793
12
29
- 1075
+ 1076
29
38
- 917
+ 918
41
77
- 924
+ 925
77
- 196940
- 697
+ 196941
+ 698
@@ -34818,12 +34630,12 @@
1
2
- 4011
+ 4015
2
3
- 999
+ 1000
3
@@ -34833,37 +34645,37 @@
4
5
- 1552
+ 1554
7
8
- 1018
+ 1019
8
12
- 792
+ 793
12
29
- 1075
+ 1076
29
38
- 917
+ 918
41
77
- 924
+ 925
77
- 196940
- 697
+ 196941
+ 698
@@ -34879,32 +34691,32 @@
1
2
- 987317
+ 988419
2
3
- 372963
+ 373378
3
4
- 105746
+ 105863
4
6
- 111241
+ 111365
6
17
- 129833
+ 129977
17
1943
- 12360
+ 12374
@@ -34920,32 +34732,32 @@
1
2
- 987317
+ 988419
2
3
- 372963
+ 373378
3
4
- 105746
+ 105863
4
6
- 111241
+ 111365
6
17
- 129833
+ 129977
17
1943
- 12360
+ 12374
@@ -34955,22 +34767,22 @@
ishandler
- 59432
+ 65331
block
- 59432
+ 65331
stmt_decl_bind
- 585624
+ 585099
stmt
- 545474
+ 544986
num
@@ -34978,7 +34790,7 @@
decl
- 585519
+ 584994
@@ -34992,12 +34804,12 @@
1
2
- 524595
+ 524125
2
19
- 20879
+ 20860
@@ -35013,12 +34825,12 @@
1
2
- 524595
+ 524125
2
19
- 20879
+ 20860
@@ -35216,7 +35028,7 @@
1
2
- 585481
+ 584956
2
@@ -35237,7 +35049,7 @@
1
2
- 585519
+ 584994
@@ -35247,11 +35059,11 @@
stmt_decl_entry_bind
- 528033
+ 527560
stmt
- 488186
+ 487748
num
@@ -35259,7 +35071,7 @@
decl_entry
- 527974
+ 527501
@@ -35273,12 +35085,12 @@
1
2
- 467571
+ 467152
2
19
- 20614
+ 20596
@@ -35294,12 +35106,12 @@
1
2
- 467571
+ 467152
2
19
- 20614
+ 20596
@@ -35497,12 +35309,12 @@
1
2
- 527953
+ 527480
3
6
- 21
+ 20
@@ -35518,7 +35330,7 @@
1
2
- 527974
+ 527501
@@ -35528,15 +35340,15 @@
blockscope
- 1438063
+ 1414944
block
- 1438063
+ 1414944
enclosing
- 1321870
+ 1300619
@@ -35550,7 +35362,7 @@
1
2
- 1438063
+ 1414944
@@ -35566,12 +35378,12 @@
1
2
- 1256011
+ 1235820
2
13
- 65858
+ 64799
@@ -35581,19 +35393,19 @@
jumpinfo
- 253995
+ 254036
id
- 253995
+ 254036
str
- 21152
+ 21155
target
- 53046
+ 53054
@@ -35607,7 +35419,7 @@
1
2
- 253995
+ 254036
@@ -35623,7 +35435,7 @@
1
2
- 253995
+ 254036
@@ -35639,12 +35451,12 @@
2
3
- 9875
+ 9877
3
4
- 4246
+ 4247
4
@@ -35659,7 +35471,7 @@
6
10
- 1699
+ 1700
10
@@ -35685,12 +35497,12 @@
1
2
- 16717
+ 16720
2
3
- 2631
+ 2632
3
@@ -35721,17 +35533,17 @@
2
3
- 26428
+ 26432
3
4
- 12897
+ 12899
4
5
- 5342
+ 5343
5
@@ -35741,7 +35553,7 @@
8
2124
- 3661
+ 3662
@@ -35757,7 +35569,7 @@
1
2
- 53046
+ 53054
@@ -35767,19 +35579,19 @@
preprocdirects
- 4431252
+ 4427317
id
- 4431252
+ 4427317
kind
- 1046
+ 1045
location
- 4428739
+ 4424807
@@ -35793,7 +35605,7 @@
1
2
- 4431252
+ 4427317
@@ -35809,7 +35621,7 @@
1
2
- 4431252
+ 4427317
@@ -35947,7 +35759,7 @@
1
2
- 4428635
+ 4424702
25
@@ -35968,7 +35780,7 @@
1
2
- 4428739
+ 4424807
@@ -35978,15 +35790,15 @@
preprocpair
- 1442296
+ 1419110
begin
- 1206147
+ 1186757
elseelifend
- 1442296
+ 1419110
@@ -36000,17 +35812,17 @@
1
2
- 985992
+ 970142
2
3
- 209805
+ 206432
3
11
- 10349
+ 10182
@@ -36026,7 +35838,7 @@
1
2
- 1442296
+ 1419110
@@ -36036,41 +35848,41 @@
preproctrue
- 782302
+ 770651
branch
- 782302
+ 770651
preprocfalse
- 327409
+ 321683
branch
- 327409
+ 321683
preproctext
- 3572638
+ 3569465
id
- 3572638
+ 3569465
head
- 2591544
+ 2589243
body
- 1515816
+ 1514470
@@ -36084,7 +35896,7 @@
1
2
- 3572638
+ 3569465
@@ -36100,7 +35912,7 @@
1
2
- 3572638
+ 3569465
@@ -36116,12 +35928,12 @@
1
2
- 2444464
+ 2442293
2
740
- 147080
+ 146949
@@ -36137,12 +35949,12 @@
1
2
- 2529362
+ 2527116
2
5
- 62181
+ 62126
@@ -36158,17 +35970,17 @@
1
2
- 1372191
+ 1370972
2
6
- 113686
+ 113585
6
11572
- 29939
+ 29912
@@ -36184,17 +35996,17 @@
1
2
- 1375227
+ 1374005
2
7
- 114000
+ 113899
7
2959
- 26589
+ 26565
@@ -36204,15 +36016,15 @@
includes
- 315649
+ 310575
id
- 315649
+ 310575
included
- 118074
+ 116176
@@ -36226,7 +36038,7 @@
1
2
- 315649
+ 310575
@@ -36242,32 +36054,32 @@
1
2
- 61624
+ 60633
2
3
- 22109
+ 21754
3
4
- 12701
+ 12497
4
6
- 10349
+ 10182
6
14
- 8937
+ 8794
14
47
- 2352
+ 2314
@@ -36277,15 +36089,15 @@
link_targets
- 1471
+ 1475
id
- 1471
+ 1475
binary
- 1471
+ 1475
@@ -36299,7 +36111,7 @@
1
2
- 1471
+ 1475
@@ -36315,7 +36127,7 @@
1
2
- 1471
+ 1475
@@ -36325,11 +36137,11 @@
link_parent
- 40143068
+ 38279611
element
- 5115983
+ 4843995
link_target
@@ -36347,17 +36159,17 @@
1
2
- 701934
+ 645317
2
9
- 44146
+ 25334
9
10
- 4369903
+ 4173343
@@ -36376,48 +36188,48 @@
35
- 124207
- 124208
+ 118453
+ 118454
35
- 124311
- 124312
+ 118557
+ 118558
35
- 124409
- 124410
+ 118652
+ 118653
35
- 124447
- 124448
+ 118687
+ 118688
35
- 124454
- 124455
+ 118693
+ 118694
35
- 124463
- 124464
+ 118709
+ 118710
35
- 126339
- 126340
+ 120575
+ 120576
35
- 132460
- 132461
+ 125080
+ 125081
35
- 134288
- 134289
+ 127476
+ 127477
35
diff --git a/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/old.dbscheme b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/old.dbscheme
new file mode 100644
index 00000000000..19e31bf071f
--- /dev/null
+++ b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/old.dbscheme
@@ -0,0 +1,2115 @@
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ /**
+ * An invocation of the compiler. Note that more than one file may
+ * be compiled per invocation. For example, this command compiles
+ * three source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ */
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | *path to extractor*
+ * 1 | `--mimic`
+ * 2 | `/usr/bin/gcc`
+ * 3 | `-c`
+ * 4 | f1.c
+ * 5 | f2.c
+ * 6 | f3.c
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.c
+ * 1 | f2.c
+ * 2 | f3.c
+ *
+ * Note that even if those files `#include` headers, those headers
+ * do not appear as rows.
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+
+/**
+ * External data, loaded from CSV files during snapshot creation. See
+ * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data)
+ * for more information.
+ */
+externalData(
+ int id : @externalDataElement,
+ string path : string ref,
+ int column: int ref,
+ string value : string ref
+);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Information about packages that provide code used during compilation.
+ * The `id` is just a unique identifier.
+ * The `namespace` is typically the name of the package manager that
+ * provided the package (e.g. "dpkg" or "yum").
+ * The `package_name` is the name of the package, and `version` is its
+ * version (as a string).
+ */
+external_packages(
+ unique int id: @external_package,
+ string namespace : string ref,
+ string package_name : string ref,
+ string version : string ref
+);
+
+/**
+ * Holds if File `fileid` was provided by package `package`.
+ */
+header_to_external_package(
+ int fileid : @file ref,
+ int package : @external_package ref
+);
+
+/*
+ * Version history
+ */
+
+svnentries(
+ unique int id : @svnentry,
+ string revision : string ref,
+ string author : string ref,
+ date revisionDate : date ref,
+ int changeSize : int ref
+)
+
+svnaffectedfiles(
+ int id : @svnentry ref,
+ int file : @file ref,
+ string action : string ref
+)
+
+svnentrymsg(
+ unique int id : @svnentry ref,
+ string message : string ref
+)
+
+svnchurn(
+ int commit : @svnentry ref,
+ int file : @file ref,
+ int addedLines : int ref,
+ int deletedLines : int ref
+)
+
+/*
+ * C++ dbscheme
+ */
+
+@location = @location_stmt | @location_expr | @location_default ;
+
+/**
+ * The location of an element that is not an expression or a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_default(
+ /** The location of an element that is not an expression or a statement. */
+ unique int id: @location_default,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_stmt(
+ /** The location of a statement. */
+ unique int id: @location_stmt,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of an expression.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_expr(
+ /** The location of an expression. */
+ unique int id: @location_expr,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/** An element for which line-count information is available. */
+@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location_default ref
+);
+
+files(
+ unique int id: @file,
+ string name: string ref
+);
+
+folders(
+ unique int id: @folder,
+ string name: string ref
+);
+
+@container = @folder | @file
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref
+);
+
+fileannotations(
+ int id: @file ref,
+ int kind: int ref,
+ string name: string ref,
+ string value: string ref
+);
+
+inmacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+affectedbymacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+/*
+ case @macroinvocations.kind of
+ 1 = macro expansion
+ | 2 = other macro reference
+ ;
+*/
+macroinvocations(
+ unique int id: @macroinvocation,
+ int macro_id: @ppd_define ref,
+ int location: @location_default ref,
+ int kind: int ref
+);
+
+macroparent(
+ unique int id: @macroinvocation ref,
+ int parent_id: @macroinvocation ref
+);
+
+// a macroinvocation may be part of another location
+// the way to find a constant expression that uses a macro
+// is thus to find a constant expression that has a location
+// to which a macro invocation is bound
+macrolocationbind(
+ int id: @macroinvocation ref,
+ int location: @location ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_unexpanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_expanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+/*
+ case @function.kind of
+ 1 = normal
+ | 2 = constructor
+ | 3 = destructor
+ | 4 = conversion
+ | 5 = operator
+ | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk
+ ;
+*/
+functions(
+ unique int id: @function,
+ string name: string ref,
+ int kind: int ref
+);
+
+function_entry_point(int id: @function ref, unique int entry_point: @stmt ref);
+
+function_return_type(int id: @function ref, int return_type: @type ref);
+
+/** If `function` is a coroutine, then this gives the
+ std::experimental::resumable_traits instance associated with it,
+ and the variables representing the `handle` and `promise` for it. */
+coroutine(
+ unique int function: @function ref,
+ int traits: @type ref,
+ int handle: @variable ref,
+ int promise: @variable ref
+);
+
+/** The `new` function used for allocating the coroutine state, if any. */
+coroutine_new(
+ unique int function: @function ref,
+ int new: @function ref
+);
+
+/** The `delete` function used for deallocating the coroutine state, if any. */
+coroutine_delete(
+ unique int function: @function ref,
+ int delete: @function ref
+);
+
+purefunctions(unique int id: @function ref);
+
+function_deleted(unique int id: @function ref);
+
+function_defaulted(unique int id: @function ref);
+
+member_function_this_type(unique int id: @function ref, int this_type: @type ref);
+
+#keyset[id, type_id]
+fun_decls(
+ int id: @fun_decl,
+ int function: @function ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+fun_def(unique int id: @fun_decl ref);
+fun_specialized(unique int id: @fun_decl ref);
+fun_implicit(unique int id: @fun_decl ref);
+fun_decl_specifiers(
+ int id: @fun_decl ref,
+ string name: string ref
+)
+#keyset[fun_decl, index]
+fun_decl_throws(
+ int fun_decl: @fun_decl ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+/* an empty throw specification is different from none */
+fun_decl_empty_throws(unique int fun_decl: @fun_decl ref);
+fun_decl_noexcept(
+ int fun_decl: @fun_decl ref,
+ int constant: @expr ref
+);
+fun_decl_empty_noexcept(int fun_decl: @fun_decl ref);
+fun_decl_typedef_type(
+ unique int fun_decl: @fun_decl ref,
+ int typedeftype_id: @usertype ref
+);
+
+param_decl_bind(
+ unique int id: @var_decl ref,
+ int index: int ref,
+ int fun_decl: @fun_decl ref
+);
+
+#keyset[id, type_id]
+var_decls(
+ int id: @var_decl,
+ int variable: @variable ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+var_def(unique int id: @var_decl ref);
+var_decl_specifiers(
+ int id: @var_decl ref,
+ string name: string ref
+)
+is_structured_binding(unique int id: @variable ref);
+
+type_decls(
+ unique int id: @type_decl,
+ int type_id: @type ref,
+ int location: @location_default ref
+);
+type_def(unique int id: @type_decl ref);
+type_decl_top(
+ unique int type_decl: @type_decl ref
+);
+
+namespace_decls(
+ unique int id: @namespace_decl,
+ int namespace_id: @namespace ref,
+ int location: @location_default ref,
+ int bodylocation: @location_default ref
+);
+
+usings(
+ unique int id: @using,
+ int element_id: @element ref,
+ int location: @location_default ref
+);
+
+/** The element which contains the `using` declaration. */
+using_container(
+ int parent: @element ref,
+ int child: @using ref
+);
+
+static_asserts(
+ unique int id: @static_assert,
+ int condition : @expr ref,
+ string message : string ref,
+ int location: @location_default ref,
+ int enclosing : @element ref
+);
+
+// each function has an ordered list of parameters
+#keyset[id, type_id]
+#keyset[function, index, type_id]
+params(
+ int id: @parameter,
+ int function: @functionorblock ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+overrides(int new: @function ref, int old: @function ref);
+
+#keyset[id, type_id]
+membervariables(
+ int id: @membervariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+globalvariables(
+ int id: @globalvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+localvariables(
+ int id: @localvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+autoderivation(
+ unique int var: @variable ref,
+ int derivation_type: @type ref
+);
+
+enumconstants(
+ unique int id: @enumconstant,
+ int parent: @usertype ref,
+ int index: int ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+
+@variable = @localscopevariable | @globalvariable | @membervariable;
+
+@localscopevariable = @localvariable | @parameter;
+
+/*
+ Built-in types are the fundamental types, e.g., integral, floating, and void.
+
+ case @builtintype.kind of
+ 1 = error
+ | 2 = unknown
+ | 3 = void
+ | 4 = boolean
+ | 5 = char
+ | 6 = unsigned_char
+ | 7 = signed_char
+ | 8 = short
+ | 9 = unsigned_short
+ | 10 = signed_short
+ | 11 = int
+ | 12 = unsigned_int
+ | 13 = signed_int
+ | 14 = long
+ | 15 = unsigned_long
+ | 16 = signed_long
+ | 17 = long_long
+ | 18 = unsigned_long_long
+ | 19 = signed_long_long
+ | 20 = __int8 // Microsoft-specific
+ | 21 = __int16 // Microsoft-specific
+ | 22 = __int32 // Microsoft-specific
+ | 23 = __int64 // Microsoft-specific
+ | 24 = float
+ | 25 = double
+ | 26 = long_double
+ | 27 = _Complex_float // C99-specific
+ | 28 = _Complex_double // C99-specific
+ | 29 = _Complex_long double // C99-specific
+ | 30 = _Imaginary_float // C99-specific
+ | 31 = _Imaginary_double // C99-specific
+ | 32 = _Imaginary_long_double // C99-specific
+ | 33 = wchar_t // Microsoft-specific
+ | 34 = decltype_nullptr // C++11
+ | 35 = __int128
+ | 36 = unsigned___int128
+ | 37 = signed___int128
+ | 38 = __float128
+ | 39 = _Complex___float128
+ | 40 = _Decimal32
+ | 41 = _Decimal64
+ | 42 = _Decimal128
+ | 43 = char16_t
+ | 44 = char32_t
+ | 45 = _Float32
+ | 46 = _Float32x
+ | 47 = _Float64
+ | 48 = _Float64x
+ | 49 = _Float128
+ | 50 = _Float128x
+ | 51 = char8_t
+ ;
+*/
+builtintypes(
+ unique int id: @builtintype,
+ string name: string ref,
+ int kind: int ref,
+ int size: int ref,
+ int sign: int ref,
+ int alignment: int ref
+);
+
+/*
+ Derived types are types that are directly derived from existing types and
+ point to, refer to, transform type data to return a new type.
+
+ case @derivedtype.kind of
+ 1 = pointer
+ | 2 = reference
+ | 3 = type_with_specifiers
+ | 4 = array
+ | 5 = gnu_vector
+ | 6 = routineptr
+ | 7 = routinereference
+ | 8 = rvalue_reference // C++11
+// ... 9 type_conforming_to_protocols deprecated
+ | 10 = block
+ ;
+*/
+derivedtypes(
+ unique int id: @derivedtype,
+ string name: string ref,
+ int kind: int ref,
+ int type_id: @type ref
+);
+
+pointerishsize(unique int id: @derivedtype ref,
+ int size: int ref,
+ int alignment: int ref);
+
+arraysizes(
+ unique int id: @derivedtype ref,
+ int num_elements: int ref,
+ int bytesize: int ref,
+ int alignment: int ref
+);
+
+typedefbase(
+ unique int id: @usertype ref,
+ int type_id: @type ref
+);
+
+/**
+ * An instance of the C++11 `decltype` operator. For example:
+ * ```
+ * int a;
+ * decltype(1+a) b;
+ * ```
+ * Here `expr` is `1+a`.
+ *
+ * Sometimes an additional pair of parentheses around the expression
+ * would change the semantics of this decltype, e.g.
+ * ```
+ * struct A { double x; };
+ * const A* a = new A();
+ * decltype( a->x ); // type is double
+ * decltype((a->x)); // type is const double&
+ * ```
+ * (Please consult the C++11 standard for more details).
+ * `parentheses_would_change_meaning` is `true` iff that is the case.
+ */
+#keyset[id, expr]
+decltypes(
+ int id: @decltype,
+ int expr: @expr ref,
+ int base_type: @type ref,
+ boolean parentheses_would_change_meaning: boolean ref
+);
+
+/*
+ case @usertype.kind of
+ 1 = struct
+ | 2 = class
+ | 3 = union
+ | 4 = enum
+ | 5 = typedef // classic C: typedef typedef type name
+ | 6 = template
+ | 7 = template_parameter
+ | 8 = template_template_parameter
+ | 9 = proxy_class // a proxy class associated with a template parameter
+// ... 10 objc_class deprecated
+// ... 11 objc_protocol deprecated
+// ... 12 objc_category deprecated
+ | 13 = scoped_enum
+ | 14 = using_alias // a using name = type style typedef
+ ;
+*/
+usertypes(
+ unique int id: @usertype,
+ string name: string ref,
+ int kind: int ref
+);
+
+usertypesize(
+ unique int id: @usertype ref,
+ int size: int ref,
+ int alignment: int ref
+);
+
+usertype_final(unique int id: @usertype ref);
+
+usertype_uuid(
+ unique int id: @usertype ref,
+ string uuid: string ref
+);
+
+mangled_name(
+ unique int id: @declaration ref,
+ int mangled_name : @mangledname
+);
+
+is_pod_class(unique int id: @usertype ref);
+is_standard_layout_class(unique int id: @usertype ref);
+
+is_complete(unique int id: @usertype ref);
+
+is_class_template(unique int id: @usertype ref);
+class_instantiation(
+ int to: @usertype ref,
+ int from: @usertype ref
+);
+class_template_argument(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+class_template_argument_value(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_proxy_class_for(
+ unique int id: @usertype ref,
+ unique int templ_param_id: @usertype ref
+);
+
+type_mentions(
+ unique int id: @type_mention,
+ int type_id: @type ref,
+ int location: @location ref,
+ // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there.
+ int kind: int ref
+);
+
+is_function_template(unique int id: @function ref);
+function_instantiation(
+ unique int to: @function ref,
+ int from: @function ref
+);
+function_template_argument(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+function_template_argument_value(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_variable_template(unique int id: @variable ref);
+variable_instantiation(
+ unique int to: @variable ref,
+ int from: @variable ref
+);
+variable_template_argument(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+variable_template_argument_value(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+/*
+ Fixed point types
+ precision(1) = short, precision(2) = default, precision(3) = long
+ is_unsigned(1) = unsigned is_unsigned(2) = signed
+ is_fract_type(1) = declared with _Fract
+ saturating(1) = declared with _Sat
+*/
+/* TODO
+fixedpointtypes(
+ unique int id: @fixedpointtype,
+ int precision: int ref,
+ int is_unsigned: int ref,
+ int is_fract_type: int ref,
+ int saturating: int ref);
+*/
+
+routinetypes(
+ unique int id: @routinetype,
+ int return_type: @type ref
+);
+
+routinetypeargs(
+ int routine: @routinetype ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+ptrtomembers(
+ unique int id: @ptrtomember,
+ int type_id: @type ref,
+ int class_id: @type ref
+);
+
+/*
+ specifiers for types, functions, and variables
+
+ "public",
+ "protected",
+ "private",
+
+ "const",
+ "volatile",
+ "static",
+
+ "pure",
+ "virtual",
+ "sealed", // Microsoft
+ "__interface", // Microsoft
+ "inline",
+ "explicit",
+
+ "near", // near far extension
+ "far", // near far extension
+ "__ptr32", // Microsoft
+ "__ptr64", // Microsoft
+ "__sptr", // Microsoft
+ "__uptr", // Microsoft
+ "dllimport", // Microsoft
+ "dllexport", // Microsoft
+ "thread", // Microsoft
+ "naked", // Microsoft
+ "microsoft_inline", // Microsoft
+ "forceinline", // Microsoft
+ "selectany", // Microsoft
+ "nothrow", // Microsoft
+ "novtable", // Microsoft
+ "noreturn", // Microsoft
+ "noinline", // Microsoft
+ "noalias", // Microsoft
+ "restrict", // Microsoft
+*/
+
+specifiers(
+ unique int id: @specifier,
+ unique string str: string ref
+);
+
+typespecifiers(
+ int type_id: @type ref,
+ int spec_id: @specifier ref
+);
+
+funspecifiers(
+ int func_id: @function ref,
+ int spec_id: @specifier ref
+);
+
+varspecifiers(
+ int var_id: @accessible ref,
+ int spec_id: @specifier ref
+);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ string name: string ref,
+ string name_space: string ref,
+ int location: @location_default ref
+);
+
+case @attribute.kind of
+ 0 = @gnuattribute
+| 1 = @stdattribute
+| 2 = @declspec
+| 3 = @msattribute
+| 4 = @alignas
+// ... 5 @objc_propertyattribute deprecated
+;
+
+attribute_args(
+ unique int id: @attribute_arg,
+ int kind: int ref,
+ int attribute: @attribute ref,
+ int index: int ref,
+ int location: @location_default ref
+);
+
+case @attribute_arg.kind of
+ 0 = @attribute_arg_empty
+| 1 = @attribute_arg_token
+| 2 = @attribute_arg_constant
+| 3 = @attribute_arg_type
+;
+
+attribute_arg_value(
+ unique int arg: @attribute_arg ref,
+ string value: string ref
+);
+attribute_arg_type(
+ unique int arg: @attribute_arg ref,
+ int type_id: @type ref
+);
+attribute_arg_name(
+ unique int arg: @attribute_arg ref,
+ string name: string ref
+);
+
+typeattributes(
+ int type_id: @type ref,
+ int spec_id: @attribute ref
+);
+
+funcattributes(
+ int func_id: @function ref,
+ int spec_id: @attribute ref
+);
+
+varattributes(
+ int var_id: @accessible ref,
+ int spec_id: @attribute ref
+);
+
+stmtattributes(
+ int stmt_id: @stmt ref,
+ int spec_id: @attribute ref
+);
+
+@type = @builtintype
+ | @derivedtype
+ | @usertype
+ /* TODO | @fixedpointtype */
+ | @routinetype
+ | @ptrtomember
+ | @decltype;
+
+unspecifiedtype(
+ unique int type_id: @type ref,
+ int unspecified_type_id: @type ref
+);
+
+member(
+ int parent: @type ref,
+ int index: int ref,
+ int child: @member ref
+);
+
+@enclosingfunction_child = @usertype | @variable | @namespace
+
+enclosingfunction(
+ unique int child: @enclosingfunction_child ref,
+ int parent: @function ref
+);
+
+derivations(
+ unique int derivation: @derivation,
+ int sub: @type ref,
+ int index: int ref,
+ int super: @type ref,
+ int location: @location_default ref
+);
+
+derspecifiers(
+ int der_id: @derivation ref,
+ int spec_id: @specifier ref
+);
+
+/**
+ * Contains the byte offset of the base class subobject within the derived
+ * class. Only holds for non-virtual base classes, but see table
+ * `virtual_base_offsets` for offsets of virtual base class subobjects.
+ */
+direct_base_offsets(
+ unique int der_id: @derivation ref,
+ int offset: int ref
+);
+
+/**
+ * Contains the byte offset of the virtual base class subobject for class
+ * `super` within a most-derived object of class `sub`. `super` can be either a
+ * direct or indirect base class.
+ */
+#keyset[sub, super]
+virtual_base_offsets(
+ int sub: @usertype ref,
+ int super: @usertype ref,
+ int offset: int ref
+);
+
+frienddecls(
+ unique int id: @frienddecl,
+ int type_id: @type ref,
+ int decl_id: @declaration ref,
+ int location: @location_default ref
+);
+
+@declaredtype = @usertype ;
+
+@declaration = @function
+ | @declaredtype
+ | @variable
+ | @enumconstant
+ | @frienddecl;
+
+@member = @membervariable
+ | @function
+ | @declaredtype
+ | @enumconstant;
+
+@locatable = @diagnostic
+ | @declaration
+ | @ppd_include
+ | @ppd_define
+ | @macroinvocation
+ /*| @funcall*/
+ | @xmllocatable
+ | @attribute
+ | @attribute_arg;
+
+@namedscope = @namespace | @usertype;
+
+@element = @locatable
+ | @file
+ | @folder
+ | @specifier
+ | @type
+ | @expr
+ | @namespace
+ | @initialiser
+ | @stmt
+ | @derivation
+ | @comment
+ | @preprocdirect
+ | @fun_decl
+ | @var_decl
+ | @type_decl
+ | @namespace_decl
+ | @using
+ | @namequalifier
+ | @specialnamequalifyingelement
+ | @static_assert
+ | @type_mention
+ | @lambdacapture;
+
+@exprparent = @element;
+
+comments(
+ unique int id: @comment,
+ string contents: string ref,
+ int location: @location_default ref
+);
+
+commentbinding(
+ int id: @comment ref,
+ int element: @element ref
+);
+
+exprconv(
+ int converted: @expr ref,
+ unique int conversion: @expr ref
+);
+
+compgenerated(unique int id: @element ref);
+
+/**
+ * `destructor_call` destructs the `i`'th entity that should be
+ * destructed following `element`. Note that entities should be
+ * destructed in reverse construction order, so for a given `element`
+ * these should be called from highest to lowest `i`.
+ */
+#keyset[element, destructor_call]
+#keyset[element, i]
+synthetic_destructor_call(
+ int element: @element ref,
+ int i: int ref,
+ int destructor_call: @routineexpr ref
+);
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref
+);
+
+namespace_inline(
+ unique int id: @namespace ref
+);
+
+namespacembrs(
+ int parentid: @namespace ref,
+ unique int memberid: @namespacembr ref
+);
+
+@namespacembr = @declaration | @namespace;
+
+exprparents(
+ int expr_id: @expr ref,
+ int child_index: int ref,
+ int parent_id: @exprparent ref
+);
+
+expr_isload(unique int expr_id: @expr ref);
+
+@cast = @c_style_cast
+ | @const_cast
+ | @dynamic_cast
+ | @reinterpret_cast
+ | @static_cast
+ ;
+
+/*
+case @conversion.kind of
+ 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast
+| 1 = @bool_conversion // conversion to 'bool'
+| 2 = @base_class_conversion // a derived-to-base conversion
+| 3 = @derived_class_conversion // a base-to-derived conversion
+| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member
+| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member
+| 6 = @glvalue_adjust // an adjustment of the type of a glvalue
+| 7 = @prvalue_adjust // an adjustment of the type of a prvalue
+;
+*/
+/**
+ * Describes the semantics represented by a cast expression. This is largely
+ * independent of the source syntax of the cast, so it is separate from the
+ * regular expression kind.
+ */
+conversionkinds(
+ unique int expr_id: @cast ref,
+ int kind: int ref
+);
+
+@conversion = @cast
+ | @array_to_pointer
+ | @parexpr
+ | @reference_to
+ | @ref_indirect
+ | @temp_init
+ ;
+
+/*
+case @funbindexpr.kind of
+ 0 = @normal_call // a normal call
+| 1 = @virtual_call // a virtual call
+| 2 = @adl_call // a call whose target is only found by ADL
+;
+*/
+iscall(unique int caller: @funbindexpr ref, int kind: int ref);
+
+numtemplatearguments(
+ unique int expr_id: @expr ref,
+ int num: int ref
+);
+
+specialnamequalifyingelements(
+ unique int id: @specialnamequalifyingelement,
+ unique string name: string ref
+);
+
+@namequalifiableelement = @expr | @namequalifier;
+@namequalifyingelement = @namespace
+ | @specialnamequalifyingelement
+ | @usertype;
+
+namequalifiers(
+ unique int id: @namequalifier,
+ unique int qualifiableelement: @namequalifiableelement ref,
+ int qualifyingelement: @namequalifyingelement ref,
+ int location: @location_default ref
+);
+
+varbind(
+ int expr: @varbindexpr ref,
+ int var: @accessible ref
+);
+
+funbind(
+ int expr: @funbindexpr ref,
+ int fun: @function ref
+);
+
+@any_new_expr = @new_expr
+ | @new_array_expr;
+
+@new_or_delete_expr = @any_new_expr
+ | @delete_expr
+ | @delete_array_expr;
+
+@prefix_crement_expr = @preincrexpr | @predecrexpr;
+
+@postfix_crement_expr = @postincrexpr | @postdecrexpr;
+
+@increment_expr = @preincrexpr | @postincrexpr;
+
+@decrement_expr = @predecrexpr | @postdecrexpr;
+
+@crement_expr = @increment_expr | @decrement_expr;
+
+@un_arith_op_expr = @arithnegexpr
+ | @unaryplusexpr
+ | @conjugation
+ | @realpartexpr
+ | @imagpartexpr
+ | @crement_expr
+ ;
+
+@un_bitwise_op_expr = @complementexpr;
+
+@un_log_op_expr = @notexpr;
+
+@un_op_expr = @address_of
+ | @indirect
+ | @un_arith_op_expr
+ | @un_bitwise_op_expr
+ | @builtinaddressof
+ | @vec_fill
+ | @un_log_op_expr
+ | @co_await
+ | @co_yield
+ ;
+
+@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr;
+
+@cmp_op_expr = @eq_op_expr | @rel_op_expr;
+
+@eq_op_expr = @eqexpr | @neexpr;
+
+@rel_op_expr = @gtexpr
+ | @ltexpr
+ | @geexpr
+ | @leexpr
+ | @spaceshipexpr
+ ;
+
+@bin_bitwise_op_expr = @lshiftexpr
+ | @rshiftexpr
+ | @andexpr
+ | @orexpr
+ | @xorexpr
+ ;
+
+@p_arith_op_expr = @paddexpr
+ | @psubexpr
+ | @pdiffexpr
+ ;
+
+@bin_arith_op_expr = @addexpr
+ | @subexpr
+ | @mulexpr
+ | @divexpr
+ | @remexpr
+ | @jmulexpr
+ | @jdivexpr
+ | @fjaddexpr
+ | @jfaddexpr
+ | @fjsubexpr
+ | @jfsubexpr
+ | @minexpr
+ | @maxexpr
+ | @p_arith_op_expr
+ ;
+
+@bin_op_expr = @bin_arith_op_expr
+ | @bin_bitwise_op_expr
+ | @cmp_op_expr
+ | @bin_log_op_expr
+ ;
+
+@op_expr = @un_op_expr
+ | @bin_op_expr
+ | @assign_expr
+ | @conditionalexpr
+ ;
+
+@assign_arith_expr = @assignaddexpr
+ | @assignsubexpr
+ | @assignmulexpr
+ | @assigndivexpr
+ | @assignremexpr
+ ;
+
+@assign_bitwise_expr = @assignandexpr
+ | @assignorexpr
+ | @assignxorexpr
+ | @assignlshiftexpr
+ | @assignrshiftexpr
+ | @assignpaddexpr
+ | @assignpsubexpr
+ ;
+
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr
+
+@assign_expr = @assignexpr | @assign_op_expr
+
+/*
+ case @allocator.form of
+ 0 = plain
+ | 1 = alignment
+ ;
+*/
+
+/**
+ * The allocator function associated with a `new` or `new[]` expression.
+ * The `form` column specified whether the allocation call contains an alignment
+ * argument.
+ */
+expr_allocator(
+ unique int expr: @any_new_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/*
+ case @deallocator.form of
+ 0 = plain
+ | 1 = size
+ | 2 = alignment
+ | 3 = size_and_alignment
+ ;
+*/
+
+/**
+ * The deallocator function associated with a `delete`, `delete[]`, `new`, or
+ * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the
+ * one used to free memory if the initialization throws an exception.
+ * The `form` column specifies whether the deallocation call contains a size
+ * argument, and alignment argument, or both.
+ */
+expr_deallocator(
+ unique int expr: @new_or_delete_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/**
+ * Holds if the `@conditionalexpr` is of the two operand form
+ * `guard ? : false`.
+ */
+expr_cond_two_operand(
+ unique int cond: @conditionalexpr ref
+);
+
+/**
+ * The guard of `@conditionalexpr` `guard ? true : false`
+ */
+expr_cond_guard(
+ unique int cond: @conditionalexpr ref,
+ int guard: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` holds. For the two operand form
+ * `guard ?: false` consider using `expr_cond_guard` instead.
+ */
+expr_cond_true(
+ unique int cond: @conditionalexpr ref,
+ int true: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` does not hold.
+ */
+expr_cond_false(
+ unique int cond: @conditionalexpr ref,
+ int false: @expr ref
+);
+
+/** A string representation of the value. */
+values(
+ unique int id: @value,
+ string str: string ref
+);
+
+/** The actual text in the source code for the value, if any. */
+valuetext(
+ unique int id: @value ref,
+ string text: string ref
+);
+
+valuebind(
+ int val: @value ref,
+ unique int expr: @expr ref
+);
+
+fieldoffsets(
+ unique int id: @variable ref,
+ int byteoffset: int ref,
+ int bitoffset: int ref
+);
+
+bitfield(
+ unique int id: @variable ref,
+ int bits: int ref,
+ int declared_bits: int ref
+);
+
+/* TODO
+memberprefix(
+ int member: @expr ref,
+ int prefix: @expr ref
+);
+*/
+
+/*
+ kind(1) = mbrcallexpr
+ kind(2) = mbrptrcallexpr
+ kind(3) = mbrptrmbrcallexpr
+ kind(4) = ptrmbrptrmbrcallexpr
+ kind(5) = mbrreadexpr // x.y
+ kind(6) = mbrptrreadexpr // p->y
+ kind(7) = mbrptrmbrreadexpr // x.*pm
+ kind(8) = mbrptrmbrptrreadexpr // x->*pm
+ kind(9) = staticmbrreadexpr // static x.y
+ kind(10) = staticmbrptrreadexpr // static p->y
+*/
+/* TODO
+memberaccess(
+ int member: @expr ref,
+ int kind: int ref
+);
+*/
+
+initialisers(
+ unique int init: @initialiser,
+ int var: @accessible ref,
+ unique int expr: @expr ref,
+ int location: @location_expr ref
+);
+
+braced_initialisers(
+ int init: @initialiser ref
+);
+
+/**
+ * An ancestor for the expression, for cases in which we cannot
+ * otherwise find the expression's parent.
+ */
+expr_ancestor(
+ int exp: @expr ref,
+ int ancestor: @element ref
+);
+
+exprs(
+ unique int id: @expr,
+ int kind: int ref,
+ int location: @location_expr ref
+);
+
+/*
+ case @value.category of
+ 1 = prval
+ | 2 = xval
+ | 3 = lval
+ ;
+*/
+expr_types(
+ int id: @expr ref,
+ int typeid: @type ref,
+ int value_category: int ref
+);
+
+case @expr.kind of
+ 1 = @errorexpr
+| 2 = @address_of // & AddressOfExpr
+| 3 = @reference_to // ReferenceToExpr (implicit?)
+| 4 = @indirect // * PointerDereferenceExpr
+| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?)
+// ...
+| 8 = @array_to_pointer // (???)
+| 9 = @vacuous_destructor_call // VacuousDestructorCall
+// ...
+| 11 = @assume // Microsoft
+| 12 = @parexpr
+| 13 = @arithnegexpr
+| 14 = @unaryplusexpr
+| 15 = @complementexpr
+| 16 = @notexpr
+| 17 = @conjugation // GNU ~ operator
+| 18 = @realpartexpr // GNU __real
+| 19 = @imagpartexpr // GNU __imag
+| 20 = @postincrexpr
+| 21 = @postdecrexpr
+| 22 = @preincrexpr
+| 23 = @predecrexpr
+| 24 = @conditionalexpr
+| 25 = @addexpr
+| 26 = @subexpr
+| 27 = @mulexpr
+| 28 = @divexpr
+| 29 = @remexpr
+| 30 = @jmulexpr // C99 mul imaginary
+| 31 = @jdivexpr // C99 div imaginary
+| 32 = @fjaddexpr // C99 add real + imaginary
+| 33 = @jfaddexpr // C99 add imaginary + real
+| 34 = @fjsubexpr // C99 sub real - imaginary
+| 35 = @jfsubexpr // C99 sub imaginary - real
+| 36 = @paddexpr // pointer add (pointer + int or int + pointer)
+| 37 = @psubexpr // pointer sub (pointer - integer)
+| 38 = @pdiffexpr // difference between two pointers
+| 39 = @lshiftexpr
+| 40 = @rshiftexpr
+| 41 = @andexpr
+| 42 = @orexpr
+| 43 = @xorexpr
+| 44 = @eqexpr
+| 45 = @neexpr
+| 46 = @gtexpr
+| 47 = @ltexpr
+| 48 = @geexpr
+| 49 = @leexpr
+| 50 = @minexpr // GNU minimum
+| 51 = @maxexpr // GNU maximum
+| 52 = @assignexpr
+| 53 = @assignaddexpr
+| 54 = @assignsubexpr
+| 55 = @assignmulexpr
+| 56 = @assigndivexpr
+| 57 = @assignremexpr
+| 58 = @assignlshiftexpr
+| 59 = @assignrshiftexpr
+| 60 = @assignandexpr
+| 61 = @assignorexpr
+| 62 = @assignxorexpr
+| 63 = @assignpaddexpr // assign pointer add
+| 64 = @assignpsubexpr // assign pointer sub
+| 65 = @andlogicalexpr
+| 66 = @orlogicalexpr
+| 67 = @commaexpr
+| 68 = @subscriptexpr // access to member of an array, e.g., a[5]
+// ... 69 @objc_subscriptexpr deprecated
+// ... 70 @cmdaccess deprecated
+// ...
+| 73 = @virtfunptrexpr
+| 74 = @callexpr
+// ... 75 @msgexpr_normal deprecated
+// ... 76 @msgexpr_super deprecated
+// ... 77 @atselectorexpr deprecated
+// ... 78 @atprotocolexpr deprecated
+| 79 = @vastartexpr
+| 80 = @vaargexpr
+| 81 = @vaendexpr
+| 82 = @vacopyexpr
+// ... 83 @atencodeexpr deprecated
+| 84 = @varaccess
+| 85 = @thisaccess
+// ... 86 @objc_box_expr deprecated
+| 87 = @new_expr
+| 88 = @delete_expr
+| 89 = @throw_expr
+| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2)
+| 91 = @braced_init_list
+| 92 = @type_id
+| 93 = @runtime_sizeof
+| 94 = @runtime_alignof
+| 95 = @sizeof_pack
+| 96 = @expr_stmt // GNU extension
+| 97 = @routineexpr
+| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....)
+| 99 = @offsetofexpr // offsetof ::= type and field
+| 100 = @hasassignexpr // __has_assign ::= type
+| 101 = @hascopyexpr // __has_copy ::= type
+| 102 = @hasnothrowassign // __has_nothrow_assign ::= type
+| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type
+| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type
+| 105 = @hastrivialassign // __has_trivial_assign ::= type
+| 106 = @hastrivialconstr // __has_trivial_constructor ::= type
+| 107 = @hastrivialcopy // __has_trivial_copy ::= type
+| 108 = @hasuserdestr // __has_user_destructor ::= type
+| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type
+| 110 = @isabstractexpr // __is_abstract ::= type
+| 111 = @isbaseofexpr // __is_base_of ::= type type
+| 112 = @isclassexpr // __is_class ::= type
+| 113 = @isconvtoexpr // __is_convertible_to ::= type type
+| 114 = @isemptyexpr // __is_empty ::= type
+| 115 = @isenumexpr // __is_enum ::= type
+| 116 = @ispodexpr // __is_pod ::= type
+| 117 = @ispolyexpr // __is_polymorphic ::= type
+| 118 = @isunionexpr // __is_union ::= type
+| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type
+| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof
+// ...
+| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type
+| 123 = @literal
+| 124 = @uuidof
+| 127 = @aggregateliteral
+| 128 = @delete_array_expr
+| 129 = @new_array_expr
+// ... 130 @objc_array_literal deprecated
+// ... 131 @objc_dictionary_literal deprecated
+| 132 = @foldexpr
+// ...
+| 200 = @ctordirectinit
+| 201 = @ctorvirtualinit
+| 202 = @ctorfieldinit
+| 203 = @ctordelegatinginit
+| 204 = @dtordirectdestruct
+| 205 = @dtorvirtualdestruct
+| 206 = @dtorfielddestruct
+// ...
+| 210 = @static_cast
+| 211 = @reinterpret_cast
+| 212 = @const_cast
+| 213 = @dynamic_cast
+| 214 = @c_style_cast
+| 215 = @lambdaexpr
+| 216 = @param_ref
+| 217 = @noopexpr
+// ...
+| 294 = @istriviallyconstructibleexpr
+| 295 = @isdestructibleexpr
+| 296 = @isnothrowdestructibleexpr
+| 297 = @istriviallydestructibleexpr
+| 298 = @istriviallyassignableexpr
+| 299 = @isnothrowassignableexpr
+| 300 = @istrivialexpr
+| 301 = @isstandardlayoutexpr
+| 302 = @istriviallycopyableexpr
+| 303 = @isliteraltypeexpr
+| 304 = @hastrivialmoveconstructorexpr
+| 305 = @hastrivialmoveassignexpr
+| 306 = @hasnothrowmoveassignexpr
+| 307 = @isconstructibleexpr
+| 308 = @isnothrowconstructibleexpr
+| 309 = @hasfinalizerexpr
+| 310 = @isdelegateexpr
+| 311 = @isinterfaceclassexpr
+| 312 = @isrefarrayexpr
+| 313 = @isrefclassexpr
+| 314 = @issealedexpr
+| 315 = @issimplevalueclassexpr
+| 316 = @isvalueclassexpr
+| 317 = @isfinalexpr
+| 319 = @noexceptexpr
+| 320 = @builtinshufflevector
+| 321 = @builtinchooseexpr
+| 322 = @builtinaddressof
+| 323 = @vec_fill
+| 324 = @builtinconvertvector
+| 325 = @builtincomplex
+| 326 = @spaceshipexpr
+| 327 = @co_await
+| 328 = @co_yield
+| 329 = @temp_init
+;
+
+@var_args_expr = @vastartexpr
+ | @vaendexpr
+ | @vaargexpr
+ | @vacopyexpr
+ ;
+
+@builtin_op = @var_args_expr
+ | @noopexpr
+ | @offsetofexpr
+ | @intaddrexpr
+ | @hasassignexpr
+ | @hascopyexpr
+ | @hasnothrowassign
+ | @hasnothrowconstr
+ | @hasnothrowcopy
+ | @hastrivialassign
+ | @hastrivialconstr
+ | @hastrivialcopy
+ | @hastrivialdestructor
+ | @hasuserdestr
+ | @hasvirtualdestr
+ | @isabstractexpr
+ | @isbaseofexpr
+ | @isclassexpr
+ | @isconvtoexpr
+ | @isemptyexpr
+ | @isenumexpr
+ | @ispodexpr
+ | @ispolyexpr
+ | @isunionexpr
+ | @typescompexpr
+ | @builtinshufflevector
+ | @builtinconvertvector
+ | @builtinaddressof
+ | @istriviallyconstructibleexpr
+ | @isdestructibleexpr
+ | @isnothrowdestructibleexpr
+ | @istriviallydestructibleexpr
+ | @istriviallyassignableexpr
+ | @isnothrowassignableexpr
+ | @isstandardlayoutexpr
+ | @istriviallycopyableexpr
+ | @isliteraltypeexpr
+ | @hastrivialmoveconstructorexpr
+ | @hastrivialmoveassignexpr
+ | @hasnothrowmoveassignexpr
+ | @isconstructibleexpr
+ | @isnothrowconstructibleexpr
+ | @hasfinalizerexpr
+ | @isdelegateexpr
+ | @isinterfaceclassexpr
+ | @isrefarrayexpr
+ | @isrefclassexpr
+ | @issealedexpr
+ | @issimplevalueclassexpr
+ | @isvalueclassexpr
+ | @isfinalexpr
+ | @builtinchooseexpr
+ | @builtincomplex
+ ;
+
+new_allocated_type(
+ unique int expr: @new_expr ref,
+ int type_id: @type ref
+);
+
+new_array_allocated_type(
+ unique int expr: @new_array_expr ref,
+ int type_id: @type ref
+);
+
+/**
+ * The field being initialized by an initializer expression within an aggregate
+ * initializer for a class/struct/union.
+ */
+#keyset[aggregate, field]
+aggregate_field_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int field: @membervariable ref
+);
+
+/**
+ * The index of the element being initialized by an initializer expression
+ * within an aggregate initializer for an array.
+ */
+#keyset[aggregate, element_index]
+aggregate_array_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int element_index: int ref
+);
+
+@ctorinit = @ctordirectinit
+ | @ctorvirtualinit
+ | @ctorfieldinit
+ | @ctordelegatinginit;
+@dtordestruct = @dtordirectdestruct
+ | @dtorvirtualdestruct
+ | @dtorfielddestruct;
+
+
+condition_decl_bind(
+ unique int expr: @condition_decl ref,
+ unique int decl: @declaration ref
+);
+
+typeid_bind(
+ unique int expr: @type_id ref,
+ int type_id: @type ref
+);
+
+uuidof_bind(
+ unique int expr: @uuidof ref,
+ int type_id: @type ref
+);
+
+@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof;
+
+sizeof_bind(
+ unique int expr: @runtime_sizeof_or_alignof ref,
+ int type_id: @type ref
+);
+
+code_block(
+ unique int block: @literal ref,
+ unique int routine: @function ref
+);
+
+lambdas(
+ unique int expr: @lambdaexpr ref,
+ string default_capture: string ref,
+ boolean has_explicit_return_type: boolean ref
+);
+
+lambda_capture(
+ unique int id: @lambdacapture,
+ int lambda: @lambdaexpr ref,
+ int index: int ref,
+ int field: @membervariable ref,
+ boolean captured_by_reference: boolean ref,
+ boolean is_implicit: boolean ref,
+ int location: @location_default ref
+);
+
+@funbindexpr = @routineexpr
+ | @new_expr
+ | @delete_expr
+ | @delete_array_expr
+ | @ctordirectinit
+ | @ctorvirtualinit
+ | @ctordelegatinginit
+ | @dtordirectdestruct
+ | @dtorvirtualdestruct;
+
+@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct;
+@addressable = @function | @variable ;
+@accessible = @addressable | @enumconstant ;
+
+@access = @varaccess | @routineexpr ;
+
+fold(
+ int expr: @foldexpr ref,
+ string operator: string ref,
+ boolean is_left_fold: boolean ref
+);
+
+stmts(
+ unique int id: @stmt,
+ int kind: int ref,
+ int location: @location_stmt ref
+);
+
+case @stmt.kind of
+ 1 = @stmt_expr
+| 2 = @stmt_if
+| 3 = @stmt_while
+| 4 = @stmt_goto
+| 5 = @stmt_label
+| 6 = @stmt_return
+| 7 = @stmt_block
+| 8 = @stmt_end_test_while // do { ... } while ( ... )
+| 9 = @stmt_for
+| 10 = @stmt_switch_case
+| 11 = @stmt_switch
+| 13 = @stmt_asm // "asm" statement or the body of an asm function
+| 15 = @stmt_try_block
+| 16 = @stmt_microsoft_try // Microsoft
+| 17 = @stmt_decl
+| 18 = @stmt_set_vla_size // C99
+| 19 = @stmt_vla_decl // C99
+| 25 = @stmt_assigned_goto // GNU
+| 26 = @stmt_empty
+| 27 = @stmt_continue
+| 28 = @stmt_break
+| 29 = @stmt_range_based_for // C++11
+// ... 30 @stmt_at_autoreleasepool_block deprecated
+// ... 31 @stmt_objc_for_in deprecated
+// ... 32 @stmt_at_synchronized deprecated
+| 33 = @stmt_handler
+// ... 34 @stmt_finally_end deprecated
+| 35 = @stmt_constexpr_if
+| 37 = @stmt_co_return
+;
+
+type_vla(
+ int type_id: @type ref,
+ int decl: @stmt_vla_decl ref
+);
+
+variable_vla(
+ int var: @variable ref,
+ int decl: @stmt_vla_decl ref
+);
+
+if_initialization(
+ unique int if_stmt: @stmt_if ref,
+ int init_id: @stmt ref
+);
+
+if_then(
+ unique int if_stmt: @stmt_if ref,
+ int then_id: @stmt ref
+);
+
+if_else(
+ unique int if_stmt: @stmt_if ref,
+ int else_id: @stmt ref
+);
+
+constexpr_if_initialization(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int init_id: @stmt ref
+);
+
+constexpr_if_then(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int then_id: @stmt ref
+);
+
+constexpr_if_else(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int else_id: @stmt ref
+);
+
+while_body(
+ unique int while_stmt: @stmt_while ref,
+ int body_id: @stmt ref
+);
+
+do_body(
+ unique int do_stmt: @stmt_end_test_while ref,
+ int body_id: @stmt ref
+);
+
+switch_initialization(
+ unique int switch_stmt: @stmt_switch ref,
+ int init_id: @stmt ref
+);
+
+#keyset[switch_stmt, index]
+switch_case(
+ int switch_stmt: @stmt_switch ref,
+ int index: int ref,
+ int case_id: @stmt_switch_case ref
+);
+
+switch_body(
+ unique int switch_stmt: @stmt_switch ref,
+ int body_id: @stmt ref
+);
+
+for_initialization(
+ unique int for_stmt: @stmt_for ref,
+ int init_id: @stmt ref
+);
+
+for_condition(
+ unique int for_stmt: @stmt_for ref,
+ int condition_id: @expr ref
+);
+
+for_update(
+ unique int for_stmt: @stmt_for ref,
+ int update_id: @expr ref
+);
+
+for_body(
+ unique int for_stmt: @stmt_for ref,
+ int body_id: @stmt ref
+);
+
+@stmtparent = @stmt | @expr_stmt ;
+stmtparents(
+ unique int id: @stmt ref,
+ int index: int ref,
+ int parent: @stmtparent ref
+);
+
+ishandler(unique int block: @stmt_block ref);
+
+@cfgnode = @stmt | @expr | @function | @initialiser ;
+
+stmt_decl_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl: @declaration ref
+);
+
+stmt_decl_entry_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl_entry: @element ref
+);
+
+@functionorblock = @function | @stmt_block;
+
+blockscope(
+ unique int block: @stmt_block ref,
+ int enclosing: @functionorblock ref
+);
+
+@jump = @stmt_goto | @stmt_break | @stmt_continue;
+
+@jumporlabel = @jump | @stmt_label | @literal;
+
+jumpinfo(
+ unique int id: @jumporlabel ref,
+ string str: string ref,
+ int target: @stmt ref
+);
+
+preprocdirects(
+ unique int id: @preprocdirect,
+ int kind: int ref,
+ int location: @location_default ref
+);
+case @preprocdirect.kind of
+ 0 = @ppd_if
+| 1 = @ppd_ifdef
+| 2 = @ppd_ifndef
+| 3 = @ppd_elif
+| 4 = @ppd_else
+| 5 = @ppd_endif
+| 6 = @ppd_plain_include
+| 7 = @ppd_define
+| 8 = @ppd_undef
+| 9 = @ppd_line
+| 10 = @ppd_error
+| 11 = @ppd_pragma
+| 12 = @ppd_objc_import
+| 13 = @ppd_include_next
+| 18 = @ppd_warning
+;
+
+@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next;
+
+@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif;
+
+preprocpair(
+ int begin : @ppd_branch ref,
+ int elseelifend : @preprocdirect ref
+);
+
+preproctrue(int branch : @ppd_branch ref);
+preprocfalse(int branch : @ppd_branch ref);
+
+preproctext(
+ unique int id: @preprocdirect ref,
+ string head: string ref,
+ string body: string ref
+);
+
+includes(
+ unique int id: @ppd_include ref,
+ int included: @file ref
+);
+
+link_targets(
+ unique int id: @link_target,
+ int binary: @file ref
+);
+
+link_parent(
+ int element : @element ref,
+ int link_target : @link_target ref
+);
+
+/* XML Files */
+
+xmlEncoding(unique int id: @file ref, string encoding: string ref);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ string root: string ref,
+ string publicId: string ref,
+ string systemId: string ref,
+ int fileid: @file ref
+);
+
+xmlElements(
+ unique int id: @xmlelement,
+ string name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ string name: string ref,
+ string value: string ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlNs(
+ int id: @xmlnamespace,
+ string prefixName: string ref,
+ string URI: string ref,
+ int fileid: @file ref
+);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref
+);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref
+);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref
+);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref
+);
+
+@xmllocatable = @xmlcharacters
+ | @xmlelement
+ | @xmlcomment
+ | @xmlattribute
+ | @xmldtd
+ | @file
+ | @xmlnamespace;
diff --git a/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/semmlecode.cpp.dbscheme b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/semmlecode.cpp.dbscheme
new file mode 100644
index 00000000000..23f7cbb88a4
--- /dev/null
+++ b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/semmlecode.cpp.dbscheme
@@ -0,0 +1,2125 @@
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ /**
+ * An invocation of the compiler. Note that more than one file may
+ * be compiled per invocation. For example, this command compiles
+ * three source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ */
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | *path to extractor*
+ * 1 | `--mimic`
+ * 2 | `/usr/bin/gcc`
+ * 3 | `-c`
+ * 4 | f1.c
+ * 5 | f2.c
+ * 6 | f3.c
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.c
+ * 1 | f2.c
+ * 2 | f3.c
+ *
+ * Note that even if those files `#include` headers, those headers
+ * do not appear as rows.
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+
+/**
+ * External data, loaded from CSV files during snapshot creation. See
+ * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data)
+ * for more information.
+ */
+externalData(
+ int id : @externalDataElement,
+ string path : string ref,
+ int column: int ref,
+ string value : string ref
+);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Information about packages that provide code used during compilation.
+ * The `id` is just a unique identifier.
+ * The `namespace` is typically the name of the package manager that
+ * provided the package (e.g. "dpkg" or "yum").
+ * The `package_name` is the name of the package, and `version` is its
+ * version (as a string).
+ */
+external_packages(
+ unique int id: @external_package,
+ string namespace : string ref,
+ string package_name : string ref,
+ string version : string ref
+);
+
+/**
+ * Holds if File `fileid` was provided by package `package`.
+ */
+header_to_external_package(
+ int fileid : @file ref,
+ int package : @external_package ref
+);
+
+/*
+ * Version history
+ */
+
+svnentries(
+ unique int id : @svnentry,
+ string revision : string ref,
+ string author : string ref,
+ date revisionDate : date ref,
+ int changeSize : int ref
+)
+
+svnaffectedfiles(
+ int id : @svnentry ref,
+ int file : @file ref,
+ string action : string ref
+)
+
+svnentrymsg(
+ unique int id : @svnentry ref,
+ string message : string ref
+)
+
+svnchurn(
+ int commit : @svnentry ref,
+ int file : @file ref,
+ int addedLines : int ref,
+ int deletedLines : int ref
+)
+
+/*
+ * C++ dbscheme
+ */
+
+@location = @location_stmt | @location_expr | @location_default ;
+
+/**
+ * The location of an element that is not an expression or a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_default(
+ /** The location of an element that is not an expression or a statement. */
+ unique int id: @location_default,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_stmt(
+ /** The location of a statement. */
+ unique int id: @location_stmt,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of an expression.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_expr(
+ /** The location of an expression. */
+ unique int id: @location_expr,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/** An element for which line-count information is available. */
+@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location_default ref
+);
+
+files(
+ unique int id: @file,
+ string name: string ref
+);
+
+folders(
+ unique int id: @folder,
+ string name: string ref
+);
+
+@container = @folder | @file
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref
+);
+
+fileannotations(
+ int id: @file ref,
+ int kind: int ref,
+ string name: string ref,
+ string value: string ref
+);
+
+inmacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+affectedbymacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+/*
+ case @macroinvocations.kind of
+ 1 = macro expansion
+ | 2 = other macro reference
+ ;
+*/
+macroinvocations(
+ unique int id: @macroinvocation,
+ int macro_id: @ppd_define ref,
+ int location: @location_default ref,
+ int kind: int ref
+);
+
+macroparent(
+ unique int id: @macroinvocation ref,
+ int parent_id: @macroinvocation ref
+);
+
+// a macroinvocation may be part of another location
+// the way to find a constant expression that uses a macro
+// is thus to find a constant expression that has a location
+// to which a macro invocation is bound
+macrolocationbind(
+ int id: @macroinvocation ref,
+ int location: @location ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_unexpanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_expanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+/*
+ case @function.kind of
+ 1 = normal
+ | 2 = constructor
+ | 3 = destructor
+ | 4 = conversion
+ | 5 = operator
+ | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk
+ ;
+*/
+functions(
+ unique int id: @function,
+ string name: string ref,
+ int kind: int ref
+);
+
+function_entry_point(int id: @function ref, unique int entry_point: @stmt ref);
+
+function_return_type(int id: @function ref, int return_type: @type ref);
+
+/** If `function` is a coroutine, then this gives the
+ std::experimental::resumable_traits instance associated with it,
+ and the variables representing the `handle` and `promise` for it. */
+coroutine(
+ unique int function: @function ref,
+ int traits: @type ref,
+ int handle: @variable ref,
+ int promise: @variable ref
+);
+
+/** The `new` function used for allocating the coroutine state, if any. */
+coroutine_new(
+ unique int function: @function ref,
+ int new: @function ref
+);
+
+/** The `delete` function used for deallocating the coroutine state, if any. */
+coroutine_delete(
+ unique int function: @function ref,
+ int delete: @function ref
+);
+
+purefunctions(unique int id: @function ref);
+
+function_deleted(unique int id: @function ref);
+
+function_defaulted(unique int id: @function ref);
+
+member_function_this_type(unique int id: @function ref, int this_type: @type ref);
+
+#keyset[id, type_id]
+fun_decls(
+ int id: @fun_decl,
+ int function: @function ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+fun_def(unique int id: @fun_decl ref);
+fun_specialized(unique int id: @fun_decl ref);
+fun_implicit(unique int id: @fun_decl ref);
+fun_decl_specifiers(
+ int id: @fun_decl ref,
+ string name: string ref
+)
+#keyset[fun_decl, index]
+fun_decl_throws(
+ int fun_decl: @fun_decl ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+/* an empty throw specification is different from none */
+fun_decl_empty_throws(unique int fun_decl: @fun_decl ref);
+fun_decl_noexcept(
+ int fun_decl: @fun_decl ref,
+ int constant: @expr ref
+);
+fun_decl_empty_noexcept(int fun_decl: @fun_decl ref);
+fun_decl_typedef_type(
+ unique int fun_decl: @fun_decl ref,
+ int typedeftype_id: @usertype ref
+);
+
+param_decl_bind(
+ unique int id: @var_decl ref,
+ int index: int ref,
+ int fun_decl: @fun_decl ref
+);
+
+#keyset[id, type_id]
+var_decls(
+ int id: @var_decl,
+ int variable: @variable ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+var_def(unique int id: @var_decl ref);
+var_decl_specifiers(
+ int id: @var_decl ref,
+ string name: string ref
+)
+is_structured_binding(unique int id: @variable ref);
+
+type_decls(
+ unique int id: @type_decl,
+ int type_id: @type ref,
+ int location: @location_default ref
+);
+type_def(unique int id: @type_decl ref);
+type_decl_top(
+ unique int type_decl: @type_decl ref
+);
+
+namespace_decls(
+ unique int id: @namespace_decl,
+ int namespace_id: @namespace ref,
+ int location: @location_default ref,
+ int bodylocation: @location_default ref
+);
+
+usings(
+ unique int id: @using,
+ int element_id: @element ref,
+ int location: @location_default ref
+);
+
+/** The element which contains the `using` declaration. */
+using_container(
+ int parent: @element ref,
+ int child: @using ref
+);
+
+static_asserts(
+ unique int id: @static_assert,
+ int condition : @expr ref,
+ string message : string ref,
+ int location: @location_default ref,
+ int enclosing : @element ref
+);
+
+// each function has an ordered list of parameters
+#keyset[id, type_id]
+#keyset[function, index, type_id]
+params(
+ int id: @parameter,
+ int function: @functionorblock ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+overrides(int new: @function ref, int old: @function ref);
+
+#keyset[id, type_id]
+membervariables(
+ int id: @membervariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+globalvariables(
+ int id: @globalvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+localvariables(
+ int id: @localvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+autoderivation(
+ unique int var: @variable ref,
+ int derivation_type: @type ref
+);
+
+enumconstants(
+ unique int id: @enumconstant,
+ int parent: @usertype ref,
+ int index: int ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+
+@variable = @localscopevariable | @globalvariable | @membervariable;
+
+@localscopevariable = @localvariable | @parameter;
+
+/*
+ Built-in types are the fundamental types, e.g., integral, floating, and void.
+
+ case @builtintype.kind of
+ 1 = error
+ | 2 = unknown
+ | 3 = void
+ | 4 = boolean
+ | 5 = char
+ | 6 = unsigned_char
+ | 7 = signed_char
+ | 8 = short
+ | 9 = unsigned_short
+ | 10 = signed_short
+ | 11 = int
+ | 12 = unsigned_int
+ | 13 = signed_int
+ | 14 = long
+ | 15 = unsigned_long
+ | 16 = signed_long
+ | 17 = long_long
+ | 18 = unsigned_long_long
+ | 19 = signed_long_long
+ | 20 = __int8 // Microsoft-specific
+ | 21 = __int16 // Microsoft-specific
+ | 22 = __int32 // Microsoft-specific
+ | 23 = __int64 // Microsoft-specific
+ | 24 = float
+ | 25 = double
+ | 26 = long_double
+ | 27 = _Complex_float // C99-specific
+ | 28 = _Complex_double // C99-specific
+ | 29 = _Complex_long double // C99-specific
+ | 30 = _Imaginary_float // C99-specific
+ | 31 = _Imaginary_double // C99-specific
+ | 32 = _Imaginary_long_double // C99-specific
+ | 33 = wchar_t // Microsoft-specific
+ | 34 = decltype_nullptr // C++11
+ | 35 = __int128
+ | 36 = unsigned___int128
+ | 37 = signed___int128
+ | 38 = __float128
+ | 39 = _Complex___float128
+ | 40 = _Decimal32
+ | 41 = _Decimal64
+ | 42 = _Decimal128
+ | 43 = char16_t
+ | 44 = char32_t
+ | 45 = _Float32
+ | 46 = _Float32x
+ | 47 = _Float64
+ | 48 = _Float64x
+ | 49 = _Float128
+ | 50 = _Float128x
+ | 51 = char8_t
+ ;
+*/
+builtintypes(
+ unique int id: @builtintype,
+ string name: string ref,
+ int kind: int ref,
+ int size: int ref,
+ int sign: int ref,
+ int alignment: int ref
+);
+
+/*
+ Derived types are types that are directly derived from existing types and
+ point to, refer to, transform type data to return a new type.
+
+ case @derivedtype.kind of
+ 1 = pointer
+ | 2 = reference
+ | 3 = type_with_specifiers
+ | 4 = array
+ | 5 = gnu_vector
+ | 6 = routineptr
+ | 7 = routinereference
+ | 8 = rvalue_reference // C++11
+// ... 9 type_conforming_to_protocols deprecated
+ | 10 = block
+ ;
+*/
+derivedtypes(
+ unique int id: @derivedtype,
+ string name: string ref,
+ int kind: int ref,
+ int type_id: @type ref
+);
+
+pointerishsize(unique int id: @derivedtype ref,
+ int size: int ref,
+ int alignment: int ref);
+
+arraysizes(
+ unique int id: @derivedtype ref,
+ int num_elements: int ref,
+ int bytesize: int ref,
+ int alignment: int ref
+);
+
+typedefbase(
+ unique int id: @usertype ref,
+ int type_id: @type ref
+);
+
+/**
+ * An instance of the C++11 `decltype` operator. For example:
+ * ```
+ * int a;
+ * decltype(1+a) b;
+ * ```
+ * Here `expr` is `1+a`.
+ *
+ * Sometimes an additional pair of parentheses around the expression
+ * would change the semantics of this decltype, e.g.
+ * ```
+ * struct A { double x; };
+ * const A* a = new A();
+ * decltype( a->x ); // type is double
+ * decltype((a->x)); // type is const double&
+ * ```
+ * (Please consult the C++11 standard for more details).
+ * `parentheses_would_change_meaning` is `true` iff that is the case.
+ */
+#keyset[id, expr]
+decltypes(
+ int id: @decltype,
+ int expr: @expr ref,
+ int base_type: @type ref,
+ boolean parentheses_would_change_meaning: boolean ref
+);
+
+/*
+ case @usertype.kind of
+ 1 = struct
+ | 2 = class
+ | 3 = union
+ | 4 = enum
+ | 5 = typedef // classic C: typedef typedef type name
+ | 6 = template
+ | 7 = template_parameter
+ | 8 = template_template_parameter
+ | 9 = proxy_class // a proxy class associated with a template parameter
+// ... 10 objc_class deprecated
+// ... 11 objc_protocol deprecated
+// ... 12 objc_category deprecated
+ | 13 = scoped_enum
+ | 14 = using_alias // a using name = type style typedef
+ ;
+*/
+usertypes(
+ unique int id: @usertype,
+ string name: string ref,
+ int kind: int ref
+);
+
+usertypesize(
+ unique int id: @usertype ref,
+ int size: int ref,
+ int alignment: int ref
+);
+
+usertype_final(unique int id: @usertype ref);
+
+usertype_uuid(
+ unique int id: @usertype ref,
+ string uuid: string ref
+);
+
+mangled_name(
+ unique int id: @declaration ref,
+ int mangled_name : @mangledname
+);
+
+is_pod_class(unique int id: @usertype ref);
+is_standard_layout_class(unique int id: @usertype ref);
+
+is_complete(unique int id: @usertype ref);
+
+is_class_template(unique int id: @usertype ref);
+class_instantiation(
+ int to: @usertype ref,
+ int from: @usertype ref
+);
+class_template_argument(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+class_template_argument_value(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_proxy_class_for(
+ unique int id: @usertype ref,
+ unique int templ_param_id: @usertype ref
+);
+
+type_mentions(
+ unique int id: @type_mention,
+ int type_id: @type ref,
+ int location: @location ref,
+ // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there.
+ int kind: int ref
+);
+
+is_function_template(unique int id: @function ref);
+function_instantiation(
+ unique int to: @function ref,
+ int from: @function ref
+);
+function_template_argument(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+function_template_argument_value(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_variable_template(unique int id: @variable ref);
+variable_instantiation(
+ unique int to: @variable ref,
+ int from: @variable ref
+);
+variable_template_argument(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+variable_template_argument_value(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+/*
+ Fixed point types
+ precision(1) = short, precision(2) = default, precision(3) = long
+ is_unsigned(1) = unsigned is_unsigned(2) = signed
+ is_fract_type(1) = declared with _Fract
+ saturating(1) = declared with _Sat
+*/
+/* TODO
+fixedpointtypes(
+ unique int id: @fixedpointtype,
+ int precision: int ref,
+ int is_unsigned: int ref,
+ int is_fract_type: int ref,
+ int saturating: int ref);
+*/
+
+routinetypes(
+ unique int id: @routinetype,
+ int return_type: @type ref
+);
+
+routinetypeargs(
+ int routine: @routinetype ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+ptrtomembers(
+ unique int id: @ptrtomember,
+ int type_id: @type ref,
+ int class_id: @type ref
+);
+
+/*
+ specifiers for types, functions, and variables
+
+ "public",
+ "protected",
+ "private",
+
+ "const",
+ "volatile",
+ "static",
+
+ "pure",
+ "virtual",
+ "sealed", // Microsoft
+ "__interface", // Microsoft
+ "inline",
+ "explicit",
+
+ "near", // near far extension
+ "far", // near far extension
+ "__ptr32", // Microsoft
+ "__ptr64", // Microsoft
+ "__sptr", // Microsoft
+ "__uptr", // Microsoft
+ "dllimport", // Microsoft
+ "dllexport", // Microsoft
+ "thread", // Microsoft
+ "naked", // Microsoft
+ "microsoft_inline", // Microsoft
+ "forceinline", // Microsoft
+ "selectany", // Microsoft
+ "nothrow", // Microsoft
+ "novtable", // Microsoft
+ "noreturn", // Microsoft
+ "noinline", // Microsoft
+ "noalias", // Microsoft
+ "restrict", // Microsoft
+*/
+
+specifiers(
+ unique int id: @specifier,
+ unique string str: string ref
+);
+
+typespecifiers(
+ int type_id: @type ref,
+ int spec_id: @specifier ref
+);
+
+funspecifiers(
+ int func_id: @function ref,
+ int spec_id: @specifier ref
+);
+
+varspecifiers(
+ int var_id: @accessible ref,
+ int spec_id: @specifier ref
+);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ string name: string ref,
+ string name_space: string ref,
+ int location: @location_default ref
+);
+
+case @attribute.kind of
+ 0 = @gnuattribute
+| 1 = @stdattribute
+| 2 = @declspec
+| 3 = @msattribute
+| 4 = @alignas
+// ... 5 @objc_propertyattribute deprecated
+;
+
+attribute_args(
+ unique int id: @attribute_arg,
+ int kind: int ref,
+ int attribute: @attribute ref,
+ int index: int ref,
+ int location: @location_default ref
+);
+
+case @attribute_arg.kind of
+ 0 = @attribute_arg_empty
+| 1 = @attribute_arg_token
+| 2 = @attribute_arg_constant
+| 3 = @attribute_arg_type
+;
+
+attribute_arg_value(
+ unique int arg: @attribute_arg ref,
+ string value: string ref
+);
+attribute_arg_type(
+ unique int arg: @attribute_arg ref,
+ int type_id: @type ref
+);
+attribute_arg_name(
+ unique int arg: @attribute_arg ref,
+ string name: string ref
+);
+
+typeattributes(
+ int type_id: @type ref,
+ int spec_id: @attribute ref
+);
+
+funcattributes(
+ int func_id: @function ref,
+ int spec_id: @attribute ref
+);
+
+varattributes(
+ int var_id: @accessible ref,
+ int spec_id: @attribute ref
+);
+
+stmtattributes(
+ int stmt_id: @stmt ref,
+ int spec_id: @attribute ref
+);
+
+@type = @builtintype
+ | @derivedtype
+ | @usertype
+ /* TODO | @fixedpointtype */
+ | @routinetype
+ | @ptrtomember
+ | @decltype;
+
+unspecifiedtype(
+ unique int type_id: @type ref,
+ int unspecified_type_id: @type ref
+);
+
+member(
+ int parent: @type ref,
+ int index: int ref,
+ int child: @member ref
+);
+
+@enclosingfunction_child = @usertype | @variable | @namespace
+
+enclosingfunction(
+ unique int child: @enclosingfunction_child ref,
+ int parent: @function ref
+);
+
+derivations(
+ unique int derivation: @derivation,
+ int sub: @type ref,
+ int index: int ref,
+ int super: @type ref,
+ int location: @location_default ref
+);
+
+derspecifiers(
+ int der_id: @derivation ref,
+ int spec_id: @specifier ref
+);
+
+/**
+ * Contains the byte offset of the base class subobject within the derived
+ * class. Only holds for non-virtual base classes, but see table
+ * `virtual_base_offsets` for offsets of virtual base class subobjects.
+ */
+direct_base_offsets(
+ unique int der_id: @derivation ref,
+ int offset: int ref
+);
+
+/**
+ * Contains the byte offset of the virtual base class subobject for class
+ * `super` within a most-derived object of class `sub`. `super` can be either a
+ * direct or indirect base class.
+ */
+#keyset[sub, super]
+virtual_base_offsets(
+ int sub: @usertype ref,
+ int super: @usertype ref,
+ int offset: int ref
+);
+
+frienddecls(
+ unique int id: @frienddecl,
+ int type_id: @type ref,
+ int decl_id: @declaration ref,
+ int location: @location_default ref
+);
+
+@declaredtype = @usertype ;
+
+@declaration = @function
+ | @declaredtype
+ | @variable
+ | @enumconstant
+ | @frienddecl;
+
+@member = @membervariable
+ | @function
+ | @declaredtype
+ | @enumconstant;
+
+@locatable = @diagnostic
+ | @declaration
+ | @ppd_include
+ | @ppd_define
+ | @macroinvocation
+ /*| @funcall*/
+ | @xmllocatable
+ | @attribute
+ | @attribute_arg;
+
+@namedscope = @namespace | @usertype;
+
+@element = @locatable
+ | @file
+ | @folder
+ | @specifier
+ | @type
+ | @expr
+ | @namespace
+ | @initialiser
+ | @stmt
+ | @derivation
+ | @comment
+ | @preprocdirect
+ | @fun_decl
+ | @var_decl
+ | @type_decl
+ | @namespace_decl
+ | @using
+ | @namequalifier
+ | @specialnamequalifyingelement
+ | @static_assert
+ | @type_mention
+ | @lambdacapture;
+
+@exprparent = @element;
+
+comments(
+ unique int id: @comment,
+ string contents: string ref,
+ int location: @location_default ref
+);
+
+commentbinding(
+ int id: @comment ref,
+ int element: @element ref
+);
+
+exprconv(
+ int converted: @expr ref,
+ unique int conversion: @expr ref
+);
+
+compgenerated(unique int id: @element ref);
+
+/**
+ * `destructor_call` destructs the `i`'th entity that should be
+ * destructed following `element`. Note that entities should be
+ * destructed in reverse construction order, so for a given `element`
+ * these should be called from highest to lowest `i`.
+ */
+#keyset[element, destructor_call]
+#keyset[element, i]
+synthetic_destructor_call(
+ int element: @element ref,
+ int i: int ref,
+ int destructor_call: @routineexpr ref
+);
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref
+);
+
+namespace_inline(
+ unique int id: @namespace ref
+);
+
+namespacembrs(
+ int parentid: @namespace ref,
+ unique int memberid: @namespacembr ref
+);
+
+@namespacembr = @declaration | @namespace;
+
+exprparents(
+ int expr_id: @expr ref,
+ int child_index: int ref,
+ int parent_id: @exprparent ref
+);
+
+expr_isload(unique int expr_id: @expr ref);
+
+@cast = @c_style_cast
+ | @const_cast
+ | @dynamic_cast
+ | @reinterpret_cast
+ | @static_cast
+ ;
+
+/*
+case @conversion.kind of
+ 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast
+| 1 = @bool_conversion // conversion to 'bool'
+| 2 = @base_class_conversion // a derived-to-base conversion
+| 3 = @derived_class_conversion // a base-to-derived conversion
+| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member
+| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member
+| 6 = @glvalue_adjust // an adjustment of the type of a glvalue
+| 7 = @prvalue_adjust // an adjustment of the type of a prvalue
+;
+*/
+/**
+ * Describes the semantics represented by a cast expression. This is largely
+ * independent of the source syntax of the cast, so it is separate from the
+ * regular expression kind.
+ */
+conversionkinds(
+ unique int expr_id: @cast ref,
+ int kind: int ref
+);
+
+@conversion = @cast
+ | @array_to_pointer
+ | @parexpr
+ | @reference_to
+ | @ref_indirect
+ | @temp_init
+ ;
+
+/*
+case @funbindexpr.kind of
+ 0 = @normal_call // a normal call
+| 1 = @virtual_call // a virtual call
+| 2 = @adl_call // a call whose target is only found by ADL
+;
+*/
+iscall(unique int caller: @funbindexpr ref, int kind: int ref);
+
+numtemplatearguments(
+ unique int expr_id: @expr ref,
+ int num: int ref
+);
+
+specialnamequalifyingelements(
+ unique int id: @specialnamequalifyingelement,
+ unique string name: string ref
+);
+
+@namequalifiableelement = @expr | @namequalifier;
+@namequalifyingelement = @namespace
+ | @specialnamequalifyingelement
+ | @usertype;
+
+namequalifiers(
+ unique int id: @namequalifier,
+ unique int qualifiableelement: @namequalifiableelement ref,
+ int qualifyingelement: @namequalifyingelement ref,
+ int location: @location_default ref
+);
+
+varbind(
+ int expr: @varbindexpr ref,
+ int var: @accessible ref
+);
+
+funbind(
+ int expr: @funbindexpr ref,
+ int fun: @function ref
+);
+
+@any_new_expr = @new_expr
+ | @new_array_expr;
+
+@new_or_delete_expr = @any_new_expr
+ | @delete_expr
+ | @delete_array_expr;
+
+@prefix_crement_expr = @preincrexpr | @predecrexpr;
+
+@postfix_crement_expr = @postincrexpr | @postdecrexpr;
+
+@increment_expr = @preincrexpr | @postincrexpr;
+
+@decrement_expr = @predecrexpr | @postdecrexpr;
+
+@crement_expr = @increment_expr | @decrement_expr;
+
+@un_arith_op_expr = @arithnegexpr
+ | @unaryplusexpr
+ | @conjugation
+ | @realpartexpr
+ | @imagpartexpr
+ | @crement_expr
+ ;
+
+@un_bitwise_op_expr = @complementexpr;
+
+@un_log_op_expr = @notexpr;
+
+@un_op_expr = @address_of
+ | @indirect
+ | @un_arith_op_expr
+ | @un_bitwise_op_expr
+ | @builtinaddressof
+ | @vec_fill
+ | @un_log_op_expr
+ | @co_await
+ | @co_yield
+ ;
+
+@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr;
+
+@cmp_op_expr = @eq_op_expr | @rel_op_expr;
+
+@eq_op_expr = @eqexpr | @neexpr;
+
+@rel_op_expr = @gtexpr
+ | @ltexpr
+ | @geexpr
+ | @leexpr
+ | @spaceshipexpr
+ ;
+
+@bin_bitwise_op_expr = @lshiftexpr
+ | @rshiftexpr
+ | @andexpr
+ | @orexpr
+ | @xorexpr
+ ;
+
+@p_arith_op_expr = @paddexpr
+ | @psubexpr
+ | @pdiffexpr
+ ;
+
+@bin_arith_op_expr = @addexpr
+ | @subexpr
+ | @mulexpr
+ | @divexpr
+ | @remexpr
+ | @jmulexpr
+ | @jdivexpr
+ | @fjaddexpr
+ | @jfaddexpr
+ | @fjsubexpr
+ | @jfsubexpr
+ | @minexpr
+ | @maxexpr
+ | @p_arith_op_expr
+ ;
+
+@bin_op_expr = @bin_arith_op_expr
+ | @bin_bitwise_op_expr
+ | @cmp_op_expr
+ | @bin_log_op_expr
+ ;
+
+@op_expr = @un_op_expr
+ | @bin_op_expr
+ | @assign_expr
+ | @conditionalexpr
+ ;
+
+@assign_arith_expr = @assignaddexpr
+ | @assignsubexpr
+ | @assignmulexpr
+ | @assigndivexpr
+ | @assignremexpr
+ ;
+
+@assign_bitwise_expr = @assignandexpr
+ | @assignorexpr
+ | @assignxorexpr
+ | @assignlshiftexpr
+ | @assignrshiftexpr
+ | @assignpaddexpr
+ | @assignpsubexpr
+ ;
+
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr
+
+@assign_expr = @assignexpr | @assign_op_expr
+
+/*
+ case @allocator.form of
+ 0 = plain
+ | 1 = alignment
+ ;
+*/
+
+/**
+ * The allocator function associated with a `new` or `new[]` expression.
+ * The `form` column specified whether the allocation call contains an alignment
+ * argument.
+ */
+expr_allocator(
+ unique int expr: @any_new_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/*
+ case @deallocator.form of
+ 0 = plain
+ | 1 = size
+ | 2 = alignment
+ | 3 = size_and_alignment
+ ;
+*/
+
+/**
+ * The deallocator function associated with a `delete`, `delete[]`, `new`, or
+ * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the
+ * one used to free memory if the initialization throws an exception.
+ * The `form` column specifies whether the deallocation call contains a size
+ * argument, and alignment argument, or both.
+ */
+expr_deallocator(
+ unique int expr: @new_or_delete_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/**
+ * Holds if the `@conditionalexpr` is of the two operand form
+ * `guard ? : false`.
+ */
+expr_cond_two_operand(
+ unique int cond: @conditionalexpr ref
+);
+
+/**
+ * The guard of `@conditionalexpr` `guard ? true : false`
+ */
+expr_cond_guard(
+ unique int cond: @conditionalexpr ref,
+ int guard: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` holds. For the two operand form
+ * `guard ?: false` consider using `expr_cond_guard` instead.
+ */
+expr_cond_true(
+ unique int cond: @conditionalexpr ref,
+ int true: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` does not hold.
+ */
+expr_cond_false(
+ unique int cond: @conditionalexpr ref,
+ int false: @expr ref
+);
+
+/** A string representation of the value. */
+values(
+ unique int id: @value,
+ string str: string ref
+);
+
+/** The actual text in the source code for the value, if any. */
+valuetext(
+ unique int id: @value ref,
+ string text: string ref
+);
+
+valuebind(
+ int val: @value ref,
+ unique int expr: @expr ref
+);
+
+fieldoffsets(
+ unique int id: @variable ref,
+ int byteoffset: int ref,
+ int bitoffset: int ref
+);
+
+bitfield(
+ unique int id: @variable ref,
+ int bits: int ref,
+ int declared_bits: int ref
+);
+
+/* TODO
+memberprefix(
+ int member: @expr ref,
+ int prefix: @expr ref
+);
+*/
+
+/*
+ kind(1) = mbrcallexpr
+ kind(2) = mbrptrcallexpr
+ kind(3) = mbrptrmbrcallexpr
+ kind(4) = ptrmbrptrmbrcallexpr
+ kind(5) = mbrreadexpr // x.y
+ kind(6) = mbrptrreadexpr // p->y
+ kind(7) = mbrptrmbrreadexpr // x.*pm
+ kind(8) = mbrptrmbrptrreadexpr // x->*pm
+ kind(9) = staticmbrreadexpr // static x.y
+ kind(10) = staticmbrptrreadexpr // static p->y
+*/
+/* TODO
+memberaccess(
+ int member: @expr ref,
+ int kind: int ref
+);
+*/
+
+initialisers(
+ unique int init: @initialiser,
+ int var: @accessible ref,
+ unique int expr: @expr ref,
+ int location: @location_expr ref
+);
+
+braced_initialisers(
+ int init: @initialiser ref
+);
+
+/**
+ * An ancestor for the expression, for cases in which we cannot
+ * otherwise find the expression's parent.
+ */
+expr_ancestor(
+ int exp: @expr ref,
+ int ancestor: @element ref
+);
+
+exprs(
+ unique int id: @expr,
+ int kind: int ref,
+ int location: @location_expr ref
+);
+
+/*
+ case @value.category of
+ 1 = prval
+ | 2 = xval
+ | 3 = lval
+ ;
+*/
+expr_types(
+ int id: @expr ref,
+ int typeid: @type ref,
+ int value_category: int ref
+);
+
+case @expr.kind of
+ 1 = @errorexpr
+| 2 = @address_of // & AddressOfExpr
+| 3 = @reference_to // ReferenceToExpr (implicit?)
+| 4 = @indirect // * PointerDereferenceExpr
+| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?)
+// ...
+| 8 = @array_to_pointer // (???)
+| 9 = @vacuous_destructor_call // VacuousDestructorCall
+// ...
+| 11 = @assume // Microsoft
+| 12 = @parexpr
+| 13 = @arithnegexpr
+| 14 = @unaryplusexpr
+| 15 = @complementexpr
+| 16 = @notexpr
+| 17 = @conjugation // GNU ~ operator
+| 18 = @realpartexpr // GNU __real
+| 19 = @imagpartexpr // GNU __imag
+| 20 = @postincrexpr
+| 21 = @postdecrexpr
+| 22 = @preincrexpr
+| 23 = @predecrexpr
+| 24 = @conditionalexpr
+| 25 = @addexpr
+| 26 = @subexpr
+| 27 = @mulexpr
+| 28 = @divexpr
+| 29 = @remexpr
+| 30 = @jmulexpr // C99 mul imaginary
+| 31 = @jdivexpr // C99 div imaginary
+| 32 = @fjaddexpr // C99 add real + imaginary
+| 33 = @jfaddexpr // C99 add imaginary + real
+| 34 = @fjsubexpr // C99 sub real - imaginary
+| 35 = @jfsubexpr // C99 sub imaginary - real
+| 36 = @paddexpr // pointer add (pointer + int or int + pointer)
+| 37 = @psubexpr // pointer sub (pointer - integer)
+| 38 = @pdiffexpr // difference between two pointers
+| 39 = @lshiftexpr
+| 40 = @rshiftexpr
+| 41 = @andexpr
+| 42 = @orexpr
+| 43 = @xorexpr
+| 44 = @eqexpr
+| 45 = @neexpr
+| 46 = @gtexpr
+| 47 = @ltexpr
+| 48 = @geexpr
+| 49 = @leexpr
+| 50 = @minexpr // GNU minimum
+| 51 = @maxexpr // GNU maximum
+| 52 = @assignexpr
+| 53 = @assignaddexpr
+| 54 = @assignsubexpr
+| 55 = @assignmulexpr
+| 56 = @assigndivexpr
+| 57 = @assignremexpr
+| 58 = @assignlshiftexpr
+| 59 = @assignrshiftexpr
+| 60 = @assignandexpr
+| 61 = @assignorexpr
+| 62 = @assignxorexpr
+| 63 = @assignpaddexpr // assign pointer add
+| 64 = @assignpsubexpr // assign pointer sub
+| 65 = @andlogicalexpr
+| 66 = @orlogicalexpr
+| 67 = @commaexpr
+| 68 = @subscriptexpr // access to member of an array, e.g., a[5]
+// ... 69 @objc_subscriptexpr deprecated
+// ... 70 @cmdaccess deprecated
+// ...
+| 73 = @virtfunptrexpr
+| 74 = @callexpr
+// ... 75 @msgexpr_normal deprecated
+// ... 76 @msgexpr_super deprecated
+// ... 77 @atselectorexpr deprecated
+// ... 78 @atprotocolexpr deprecated
+| 79 = @vastartexpr
+| 80 = @vaargexpr
+| 81 = @vaendexpr
+| 82 = @vacopyexpr
+// ... 83 @atencodeexpr deprecated
+| 84 = @varaccess
+| 85 = @thisaccess
+// ... 86 @objc_box_expr deprecated
+| 87 = @new_expr
+| 88 = @delete_expr
+| 89 = @throw_expr
+| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2)
+| 91 = @braced_init_list
+| 92 = @type_id
+| 93 = @runtime_sizeof
+| 94 = @runtime_alignof
+| 95 = @sizeof_pack
+| 96 = @expr_stmt // GNU extension
+| 97 = @routineexpr
+| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....)
+| 99 = @offsetofexpr // offsetof ::= type and field
+| 100 = @hasassignexpr // __has_assign ::= type
+| 101 = @hascopyexpr // __has_copy ::= type
+| 102 = @hasnothrowassign // __has_nothrow_assign ::= type
+| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type
+| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type
+| 105 = @hastrivialassign // __has_trivial_assign ::= type
+| 106 = @hastrivialconstr // __has_trivial_constructor ::= type
+| 107 = @hastrivialcopy // __has_trivial_copy ::= type
+| 108 = @hasuserdestr // __has_user_destructor ::= type
+| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type
+| 110 = @isabstractexpr // __is_abstract ::= type
+| 111 = @isbaseofexpr // __is_base_of ::= type type
+| 112 = @isclassexpr // __is_class ::= type
+| 113 = @isconvtoexpr // __is_convertible_to ::= type type
+| 114 = @isemptyexpr // __is_empty ::= type
+| 115 = @isenumexpr // __is_enum ::= type
+| 116 = @ispodexpr // __is_pod ::= type
+| 117 = @ispolyexpr // __is_polymorphic ::= type
+| 118 = @isunionexpr // __is_union ::= type
+| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type
+| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof
+// ...
+| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type
+| 123 = @literal
+| 124 = @uuidof
+| 127 = @aggregateliteral
+| 128 = @delete_array_expr
+| 129 = @new_array_expr
+// ... 130 @objc_array_literal deprecated
+// ... 131 @objc_dictionary_literal deprecated
+| 132 = @foldexpr
+// ...
+| 200 = @ctordirectinit
+| 201 = @ctorvirtualinit
+| 202 = @ctorfieldinit
+| 203 = @ctordelegatinginit
+| 204 = @dtordirectdestruct
+| 205 = @dtorvirtualdestruct
+| 206 = @dtorfielddestruct
+// ...
+| 210 = @static_cast
+| 211 = @reinterpret_cast
+| 212 = @const_cast
+| 213 = @dynamic_cast
+| 214 = @c_style_cast
+| 215 = @lambdaexpr
+| 216 = @param_ref
+| 217 = @noopexpr
+// ...
+| 294 = @istriviallyconstructibleexpr
+| 295 = @isdestructibleexpr
+| 296 = @isnothrowdestructibleexpr
+| 297 = @istriviallydestructibleexpr
+| 298 = @istriviallyassignableexpr
+| 299 = @isnothrowassignableexpr
+| 300 = @istrivialexpr
+| 301 = @isstandardlayoutexpr
+| 302 = @istriviallycopyableexpr
+| 303 = @isliteraltypeexpr
+| 304 = @hastrivialmoveconstructorexpr
+| 305 = @hastrivialmoveassignexpr
+| 306 = @hasnothrowmoveassignexpr
+| 307 = @isconstructibleexpr
+| 308 = @isnothrowconstructibleexpr
+| 309 = @hasfinalizerexpr
+| 310 = @isdelegateexpr
+| 311 = @isinterfaceclassexpr
+| 312 = @isrefarrayexpr
+| 313 = @isrefclassexpr
+| 314 = @issealedexpr
+| 315 = @issimplevalueclassexpr
+| 316 = @isvalueclassexpr
+| 317 = @isfinalexpr
+| 319 = @noexceptexpr
+| 320 = @builtinshufflevector
+| 321 = @builtinchooseexpr
+| 322 = @builtinaddressof
+| 323 = @vec_fill
+| 324 = @builtinconvertvector
+| 325 = @builtincomplex
+| 326 = @spaceshipexpr
+| 327 = @co_await
+| 328 = @co_yield
+| 329 = @temp_init
+| 330 = @isassignable
+| 331 = @isaggregate
+| 332 = @hasuniqueobjectrepresentations
+| 333 = @builtinbitcast
+| 334 = @builtinshuffle
+;
+
+@var_args_expr = @vastartexpr
+ | @vaendexpr
+ | @vaargexpr
+ | @vacopyexpr
+ ;
+
+@builtin_op = @var_args_expr
+ | @noopexpr
+ | @offsetofexpr
+ | @intaddrexpr
+ | @hasassignexpr
+ | @hascopyexpr
+ | @hasnothrowassign
+ | @hasnothrowconstr
+ | @hasnothrowcopy
+ | @hastrivialassign
+ | @hastrivialconstr
+ | @hastrivialcopy
+ | @hastrivialdestructor
+ | @hasuserdestr
+ | @hasvirtualdestr
+ | @isabstractexpr
+ | @isbaseofexpr
+ | @isclassexpr
+ | @isconvtoexpr
+ | @isemptyexpr
+ | @isenumexpr
+ | @ispodexpr
+ | @ispolyexpr
+ | @isunionexpr
+ | @typescompexpr
+ | @builtinshufflevector
+ | @builtinconvertvector
+ | @builtinaddressof
+ | @istriviallyconstructibleexpr
+ | @isdestructibleexpr
+ | @isnothrowdestructibleexpr
+ | @istriviallydestructibleexpr
+ | @istriviallyassignableexpr
+ | @isnothrowassignableexpr
+ | @isstandardlayoutexpr
+ | @istriviallycopyableexpr
+ | @isliteraltypeexpr
+ | @hastrivialmoveconstructorexpr
+ | @hastrivialmoveassignexpr
+ | @hasnothrowmoveassignexpr
+ | @isconstructibleexpr
+ | @isnothrowconstructibleexpr
+ | @hasfinalizerexpr
+ | @isdelegateexpr
+ | @isinterfaceclassexpr
+ | @isrefarrayexpr
+ | @isrefclassexpr
+ | @issealedexpr
+ | @issimplevalueclassexpr
+ | @isvalueclassexpr
+ | @isfinalexpr
+ | @builtinchooseexpr
+ | @builtincomplex
+ | @isassignable
+ | @isaggregate
+ | @hasuniqueobjectrepresentations
+ | @builtinbitcast
+ | @builtinshuffle
+ ;
+
+new_allocated_type(
+ unique int expr: @new_expr ref,
+ int type_id: @type ref
+);
+
+new_array_allocated_type(
+ unique int expr: @new_array_expr ref,
+ int type_id: @type ref
+);
+
+/**
+ * The field being initialized by an initializer expression within an aggregate
+ * initializer for a class/struct/union.
+ */
+#keyset[aggregate, field]
+aggregate_field_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int field: @membervariable ref
+);
+
+/**
+ * The index of the element being initialized by an initializer expression
+ * within an aggregate initializer for an array.
+ */
+#keyset[aggregate, element_index]
+aggregate_array_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int element_index: int ref
+);
+
+@ctorinit = @ctordirectinit
+ | @ctorvirtualinit
+ | @ctorfieldinit
+ | @ctordelegatinginit;
+@dtordestruct = @dtordirectdestruct
+ | @dtorvirtualdestruct
+ | @dtorfielddestruct;
+
+
+condition_decl_bind(
+ unique int expr: @condition_decl ref,
+ unique int decl: @declaration ref
+);
+
+typeid_bind(
+ unique int expr: @type_id ref,
+ int type_id: @type ref
+);
+
+uuidof_bind(
+ unique int expr: @uuidof ref,
+ int type_id: @type ref
+);
+
+@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof;
+
+sizeof_bind(
+ unique int expr: @runtime_sizeof_or_alignof ref,
+ int type_id: @type ref
+);
+
+code_block(
+ unique int block: @literal ref,
+ unique int routine: @function ref
+);
+
+lambdas(
+ unique int expr: @lambdaexpr ref,
+ string default_capture: string ref,
+ boolean has_explicit_return_type: boolean ref
+);
+
+lambda_capture(
+ unique int id: @lambdacapture,
+ int lambda: @lambdaexpr ref,
+ int index: int ref,
+ int field: @membervariable ref,
+ boolean captured_by_reference: boolean ref,
+ boolean is_implicit: boolean ref,
+ int location: @location_default ref
+);
+
+@funbindexpr = @routineexpr
+ | @new_expr
+ | @delete_expr
+ | @delete_array_expr
+ | @ctordirectinit
+ | @ctorvirtualinit
+ | @ctordelegatinginit
+ | @dtordirectdestruct
+ | @dtorvirtualdestruct;
+
+@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct;
+@addressable = @function | @variable ;
+@accessible = @addressable | @enumconstant ;
+
+@access = @varaccess | @routineexpr ;
+
+fold(
+ int expr: @foldexpr ref,
+ string operator: string ref,
+ boolean is_left_fold: boolean ref
+);
+
+stmts(
+ unique int id: @stmt,
+ int kind: int ref,
+ int location: @location_stmt ref
+);
+
+case @stmt.kind of
+ 1 = @stmt_expr
+| 2 = @stmt_if
+| 3 = @stmt_while
+| 4 = @stmt_goto
+| 5 = @stmt_label
+| 6 = @stmt_return
+| 7 = @stmt_block
+| 8 = @stmt_end_test_while // do { ... } while ( ... )
+| 9 = @stmt_for
+| 10 = @stmt_switch_case
+| 11 = @stmt_switch
+| 13 = @stmt_asm // "asm" statement or the body of an asm function
+| 15 = @stmt_try_block
+| 16 = @stmt_microsoft_try // Microsoft
+| 17 = @stmt_decl
+| 18 = @stmt_set_vla_size // C99
+| 19 = @stmt_vla_decl // C99
+| 25 = @stmt_assigned_goto // GNU
+| 26 = @stmt_empty
+| 27 = @stmt_continue
+| 28 = @stmt_break
+| 29 = @stmt_range_based_for // C++11
+// ... 30 @stmt_at_autoreleasepool_block deprecated
+// ... 31 @stmt_objc_for_in deprecated
+// ... 32 @stmt_at_synchronized deprecated
+| 33 = @stmt_handler
+// ... 34 @stmt_finally_end deprecated
+| 35 = @stmt_constexpr_if
+| 37 = @stmt_co_return
+;
+
+type_vla(
+ int type_id: @type ref,
+ int decl: @stmt_vla_decl ref
+);
+
+variable_vla(
+ int var: @variable ref,
+ int decl: @stmt_vla_decl ref
+);
+
+if_initialization(
+ unique int if_stmt: @stmt_if ref,
+ int init_id: @stmt ref
+);
+
+if_then(
+ unique int if_stmt: @stmt_if ref,
+ int then_id: @stmt ref
+);
+
+if_else(
+ unique int if_stmt: @stmt_if ref,
+ int else_id: @stmt ref
+);
+
+constexpr_if_initialization(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int init_id: @stmt ref
+);
+
+constexpr_if_then(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int then_id: @stmt ref
+);
+
+constexpr_if_else(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int else_id: @stmt ref
+);
+
+while_body(
+ unique int while_stmt: @stmt_while ref,
+ int body_id: @stmt ref
+);
+
+do_body(
+ unique int do_stmt: @stmt_end_test_while ref,
+ int body_id: @stmt ref
+);
+
+switch_initialization(
+ unique int switch_stmt: @stmt_switch ref,
+ int init_id: @stmt ref
+);
+
+#keyset[switch_stmt, index]
+switch_case(
+ int switch_stmt: @stmt_switch ref,
+ int index: int ref,
+ int case_id: @stmt_switch_case ref
+);
+
+switch_body(
+ unique int switch_stmt: @stmt_switch ref,
+ int body_id: @stmt ref
+);
+
+for_initialization(
+ unique int for_stmt: @stmt_for ref,
+ int init_id: @stmt ref
+);
+
+for_condition(
+ unique int for_stmt: @stmt_for ref,
+ int condition_id: @expr ref
+);
+
+for_update(
+ unique int for_stmt: @stmt_for ref,
+ int update_id: @expr ref
+);
+
+for_body(
+ unique int for_stmt: @stmt_for ref,
+ int body_id: @stmt ref
+);
+
+@stmtparent = @stmt | @expr_stmt ;
+stmtparents(
+ unique int id: @stmt ref,
+ int index: int ref,
+ int parent: @stmtparent ref
+);
+
+ishandler(unique int block: @stmt_block ref);
+
+@cfgnode = @stmt | @expr | @function | @initialiser ;
+
+stmt_decl_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl: @declaration ref
+);
+
+stmt_decl_entry_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl_entry: @element ref
+);
+
+@functionorblock = @function | @stmt_block;
+
+blockscope(
+ unique int block: @stmt_block ref,
+ int enclosing: @functionorblock ref
+);
+
+@jump = @stmt_goto | @stmt_break | @stmt_continue;
+
+@jumporlabel = @jump | @stmt_label | @literal;
+
+jumpinfo(
+ unique int id: @jumporlabel ref,
+ string str: string ref,
+ int target: @stmt ref
+);
+
+preprocdirects(
+ unique int id: @preprocdirect,
+ int kind: int ref,
+ int location: @location_default ref
+);
+case @preprocdirect.kind of
+ 0 = @ppd_if
+| 1 = @ppd_ifdef
+| 2 = @ppd_ifndef
+| 3 = @ppd_elif
+| 4 = @ppd_else
+| 5 = @ppd_endif
+| 6 = @ppd_plain_include
+| 7 = @ppd_define
+| 8 = @ppd_undef
+| 9 = @ppd_line
+| 10 = @ppd_error
+| 11 = @ppd_pragma
+| 12 = @ppd_objc_import
+| 13 = @ppd_include_next
+| 18 = @ppd_warning
+;
+
+@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next;
+
+@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif;
+
+preprocpair(
+ int begin : @ppd_branch ref,
+ int elseelifend : @preprocdirect ref
+);
+
+preproctrue(int branch : @ppd_branch ref);
+preprocfalse(int branch : @ppd_branch ref);
+
+preproctext(
+ unique int id: @preprocdirect ref,
+ string head: string ref,
+ string body: string ref
+);
+
+includes(
+ unique int id: @ppd_include ref,
+ int included: @file ref
+);
+
+link_targets(
+ unique int id: @link_target,
+ int binary: @file ref
+);
+
+link_parent(
+ int element : @element ref,
+ int link_target : @link_target ref
+);
+
+/* XML Files */
+
+xmlEncoding(unique int id: @file ref, string encoding: string ref);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ string root: string ref,
+ string publicId: string ref,
+ string systemId: string ref,
+ int fileid: @file ref
+);
+
+xmlElements(
+ unique int id: @xmlelement,
+ string name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ string name: string ref,
+ string value: string ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlNs(
+ int id: @xmlnamespace,
+ string prefixName: string ref,
+ string URI: string ref,
+ int fileid: @file ref
+);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref
+);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref
+);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref
+);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref
+);
+
+@xmllocatable = @xmlcharacters
+ | @xmlelement
+ | @xmlcomment
+ | @xmlattribute
+ | @xmldtd
+ | @file
+ | @xmlnamespace;
diff --git a/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/upgrade.properties b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/upgrade.properties
new file mode 100644
index 00000000000..db0e7e92d0e
--- /dev/null
+++ b/cpp/ql/lib/upgrades/19e31bf071f588bb7efd1e4d5a185ce4f6fbbd84/upgrade.properties
@@ -0,0 +1,2 @@
+description: Add new builtin operations
+compatibility: backwards
diff --git a/cpp/ql/lib/upgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/old.dbscheme b/cpp/ql/lib/upgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/old.dbscheme
new file mode 100644
index 00000000000..23f7cbb88a4
--- /dev/null
+++ b/cpp/ql/lib/upgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/old.dbscheme
@@ -0,0 +1,2125 @@
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ /**
+ * An invocation of the compiler. Note that more than one file may
+ * be compiled per invocation. For example, this command compiles
+ * three source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ */
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | *path to extractor*
+ * 1 | `--mimic`
+ * 2 | `/usr/bin/gcc`
+ * 3 | `-c`
+ * 4 | f1.c
+ * 5 | f2.c
+ * 6 | f3.c
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.c
+ * 1 | f2.c
+ * 2 | f3.c
+ *
+ * Note that even if those files `#include` headers, those headers
+ * do not appear as rows.
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+
+/**
+ * External data, loaded from CSV files during snapshot creation. See
+ * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data)
+ * for more information.
+ */
+externalData(
+ int id : @externalDataElement,
+ string path : string ref,
+ int column: int ref,
+ string value : string ref
+);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Information about packages that provide code used during compilation.
+ * The `id` is just a unique identifier.
+ * The `namespace` is typically the name of the package manager that
+ * provided the package (e.g. "dpkg" or "yum").
+ * The `package_name` is the name of the package, and `version` is its
+ * version (as a string).
+ */
+external_packages(
+ unique int id: @external_package,
+ string namespace : string ref,
+ string package_name : string ref,
+ string version : string ref
+);
+
+/**
+ * Holds if File `fileid` was provided by package `package`.
+ */
+header_to_external_package(
+ int fileid : @file ref,
+ int package : @external_package ref
+);
+
+/*
+ * Version history
+ */
+
+svnentries(
+ unique int id : @svnentry,
+ string revision : string ref,
+ string author : string ref,
+ date revisionDate : date ref,
+ int changeSize : int ref
+)
+
+svnaffectedfiles(
+ int id : @svnentry ref,
+ int file : @file ref,
+ string action : string ref
+)
+
+svnentrymsg(
+ unique int id : @svnentry ref,
+ string message : string ref
+)
+
+svnchurn(
+ int commit : @svnentry ref,
+ int file : @file ref,
+ int addedLines : int ref,
+ int deletedLines : int ref
+)
+
+/*
+ * C++ dbscheme
+ */
+
+@location = @location_stmt | @location_expr | @location_default ;
+
+/**
+ * The location of an element that is not an expression or a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_default(
+ /** The location of an element that is not an expression or a statement. */
+ unique int id: @location_default,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_stmt(
+ /** The location of a statement. */
+ unique int id: @location_stmt,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of an expression.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_expr(
+ /** The location of an expression. */
+ unique int id: @location_expr,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/** An element for which line-count information is available. */
+@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location_default ref
+);
+
+files(
+ unique int id: @file,
+ string name: string ref
+);
+
+folders(
+ unique int id: @folder,
+ string name: string ref
+);
+
+@container = @folder | @file
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref
+);
+
+fileannotations(
+ int id: @file ref,
+ int kind: int ref,
+ string name: string ref,
+ string value: string ref
+);
+
+inmacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+affectedbymacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+/*
+ case @macroinvocations.kind of
+ 1 = macro expansion
+ | 2 = other macro reference
+ ;
+*/
+macroinvocations(
+ unique int id: @macroinvocation,
+ int macro_id: @ppd_define ref,
+ int location: @location_default ref,
+ int kind: int ref
+);
+
+macroparent(
+ unique int id: @macroinvocation ref,
+ int parent_id: @macroinvocation ref
+);
+
+// a macroinvocation may be part of another location
+// the way to find a constant expression that uses a macro
+// is thus to find a constant expression that has a location
+// to which a macro invocation is bound
+macrolocationbind(
+ int id: @macroinvocation ref,
+ int location: @location ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_unexpanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_expanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+/*
+ case @function.kind of
+ 1 = normal
+ | 2 = constructor
+ | 3 = destructor
+ | 4 = conversion
+ | 5 = operator
+ | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk
+ ;
+*/
+functions(
+ unique int id: @function,
+ string name: string ref,
+ int kind: int ref
+);
+
+function_entry_point(int id: @function ref, unique int entry_point: @stmt ref);
+
+function_return_type(int id: @function ref, int return_type: @type ref);
+
+/** If `function` is a coroutine, then this gives the
+ std::experimental::resumable_traits instance associated with it,
+ and the variables representing the `handle` and `promise` for it. */
+coroutine(
+ unique int function: @function ref,
+ int traits: @type ref,
+ int handle: @variable ref,
+ int promise: @variable ref
+);
+
+/** The `new` function used for allocating the coroutine state, if any. */
+coroutine_new(
+ unique int function: @function ref,
+ int new: @function ref
+);
+
+/** The `delete` function used for deallocating the coroutine state, if any. */
+coroutine_delete(
+ unique int function: @function ref,
+ int delete: @function ref
+);
+
+purefunctions(unique int id: @function ref);
+
+function_deleted(unique int id: @function ref);
+
+function_defaulted(unique int id: @function ref);
+
+member_function_this_type(unique int id: @function ref, int this_type: @type ref);
+
+#keyset[id, type_id]
+fun_decls(
+ int id: @fun_decl,
+ int function: @function ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+fun_def(unique int id: @fun_decl ref);
+fun_specialized(unique int id: @fun_decl ref);
+fun_implicit(unique int id: @fun_decl ref);
+fun_decl_specifiers(
+ int id: @fun_decl ref,
+ string name: string ref
+)
+#keyset[fun_decl, index]
+fun_decl_throws(
+ int fun_decl: @fun_decl ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+/* an empty throw specification is different from none */
+fun_decl_empty_throws(unique int fun_decl: @fun_decl ref);
+fun_decl_noexcept(
+ int fun_decl: @fun_decl ref,
+ int constant: @expr ref
+);
+fun_decl_empty_noexcept(int fun_decl: @fun_decl ref);
+fun_decl_typedef_type(
+ unique int fun_decl: @fun_decl ref,
+ int typedeftype_id: @usertype ref
+);
+
+param_decl_bind(
+ unique int id: @var_decl ref,
+ int index: int ref,
+ int fun_decl: @fun_decl ref
+);
+
+#keyset[id, type_id]
+var_decls(
+ int id: @var_decl,
+ int variable: @variable ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+var_def(unique int id: @var_decl ref);
+var_decl_specifiers(
+ int id: @var_decl ref,
+ string name: string ref
+)
+is_structured_binding(unique int id: @variable ref);
+
+type_decls(
+ unique int id: @type_decl,
+ int type_id: @type ref,
+ int location: @location_default ref
+);
+type_def(unique int id: @type_decl ref);
+type_decl_top(
+ unique int type_decl: @type_decl ref
+);
+
+namespace_decls(
+ unique int id: @namespace_decl,
+ int namespace_id: @namespace ref,
+ int location: @location_default ref,
+ int bodylocation: @location_default ref
+);
+
+usings(
+ unique int id: @using,
+ int element_id: @element ref,
+ int location: @location_default ref
+);
+
+/** The element which contains the `using` declaration. */
+using_container(
+ int parent: @element ref,
+ int child: @using ref
+);
+
+static_asserts(
+ unique int id: @static_assert,
+ int condition : @expr ref,
+ string message : string ref,
+ int location: @location_default ref,
+ int enclosing : @element ref
+);
+
+// each function has an ordered list of parameters
+#keyset[id, type_id]
+#keyset[function, index, type_id]
+params(
+ int id: @parameter,
+ int function: @functionorblock ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+overrides(int new: @function ref, int old: @function ref);
+
+#keyset[id, type_id]
+membervariables(
+ int id: @membervariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+globalvariables(
+ int id: @globalvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+localvariables(
+ int id: @localvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+autoderivation(
+ unique int var: @variable ref,
+ int derivation_type: @type ref
+);
+
+enumconstants(
+ unique int id: @enumconstant,
+ int parent: @usertype ref,
+ int index: int ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+
+@variable = @localscopevariable | @globalvariable | @membervariable;
+
+@localscopevariable = @localvariable | @parameter;
+
+/*
+ Built-in types are the fundamental types, e.g., integral, floating, and void.
+
+ case @builtintype.kind of
+ 1 = error
+ | 2 = unknown
+ | 3 = void
+ | 4 = boolean
+ | 5 = char
+ | 6 = unsigned_char
+ | 7 = signed_char
+ | 8 = short
+ | 9 = unsigned_short
+ | 10 = signed_short
+ | 11 = int
+ | 12 = unsigned_int
+ | 13 = signed_int
+ | 14 = long
+ | 15 = unsigned_long
+ | 16 = signed_long
+ | 17 = long_long
+ | 18 = unsigned_long_long
+ | 19 = signed_long_long
+ | 20 = __int8 // Microsoft-specific
+ | 21 = __int16 // Microsoft-specific
+ | 22 = __int32 // Microsoft-specific
+ | 23 = __int64 // Microsoft-specific
+ | 24 = float
+ | 25 = double
+ | 26 = long_double
+ | 27 = _Complex_float // C99-specific
+ | 28 = _Complex_double // C99-specific
+ | 29 = _Complex_long double // C99-specific
+ | 30 = _Imaginary_float // C99-specific
+ | 31 = _Imaginary_double // C99-specific
+ | 32 = _Imaginary_long_double // C99-specific
+ | 33 = wchar_t // Microsoft-specific
+ | 34 = decltype_nullptr // C++11
+ | 35 = __int128
+ | 36 = unsigned___int128
+ | 37 = signed___int128
+ | 38 = __float128
+ | 39 = _Complex___float128
+ | 40 = _Decimal32
+ | 41 = _Decimal64
+ | 42 = _Decimal128
+ | 43 = char16_t
+ | 44 = char32_t
+ | 45 = _Float32
+ | 46 = _Float32x
+ | 47 = _Float64
+ | 48 = _Float64x
+ | 49 = _Float128
+ | 50 = _Float128x
+ | 51 = char8_t
+ ;
+*/
+builtintypes(
+ unique int id: @builtintype,
+ string name: string ref,
+ int kind: int ref,
+ int size: int ref,
+ int sign: int ref,
+ int alignment: int ref
+);
+
+/*
+ Derived types are types that are directly derived from existing types and
+ point to, refer to, transform type data to return a new type.
+
+ case @derivedtype.kind of
+ 1 = pointer
+ | 2 = reference
+ | 3 = type_with_specifiers
+ | 4 = array
+ | 5 = gnu_vector
+ | 6 = routineptr
+ | 7 = routinereference
+ | 8 = rvalue_reference // C++11
+// ... 9 type_conforming_to_protocols deprecated
+ | 10 = block
+ ;
+*/
+derivedtypes(
+ unique int id: @derivedtype,
+ string name: string ref,
+ int kind: int ref,
+ int type_id: @type ref
+);
+
+pointerishsize(unique int id: @derivedtype ref,
+ int size: int ref,
+ int alignment: int ref);
+
+arraysizes(
+ unique int id: @derivedtype ref,
+ int num_elements: int ref,
+ int bytesize: int ref,
+ int alignment: int ref
+);
+
+typedefbase(
+ unique int id: @usertype ref,
+ int type_id: @type ref
+);
+
+/**
+ * An instance of the C++11 `decltype` operator. For example:
+ * ```
+ * int a;
+ * decltype(1+a) b;
+ * ```
+ * Here `expr` is `1+a`.
+ *
+ * Sometimes an additional pair of parentheses around the expression
+ * would change the semantics of this decltype, e.g.
+ * ```
+ * struct A { double x; };
+ * const A* a = new A();
+ * decltype( a->x ); // type is double
+ * decltype((a->x)); // type is const double&
+ * ```
+ * (Please consult the C++11 standard for more details).
+ * `parentheses_would_change_meaning` is `true` iff that is the case.
+ */
+#keyset[id, expr]
+decltypes(
+ int id: @decltype,
+ int expr: @expr ref,
+ int base_type: @type ref,
+ boolean parentheses_would_change_meaning: boolean ref
+);
+
+/*
+ case @usertype.kind of
+ 1 = struct
+ | 2 = class
+ | 3 = union
+ | 4 = enum
+ | 5 = typedef // classic C: typedef typedef type name
+ | 6 = template
+ | 7 = template_parameter
+ | 8 = template_template_parameter
+ | 9 = proxy_class // a proxy class associated with a template parameter
+// ... 10 objc_class deprecated
+// ... 11 objc_protocol deprecated
+// ... 12 objc_category deprecated
+ | 13 = scoped_enum
+ | 14 = using_alias // a using name = type style typedef
+ ;
+*/
+usertypes(
+ unique int id: @usertype,
+ string name: string ref,
+ int kind: int ref
+);
+
+usertypesize(
+ unique int id: @usertype ref,
+ int size: int ref,
+ int alignment: int ref
+);
+
+usertype_final(unique int id: @usertype ref);
+
+usertype_uuid(
+ unique int id: @usertype ref,
+ string uuid: string ref
+);
+
+mangled_name(
+ unique int id: @declaration ref,
+ int mangled_name : @mangledname
+);
+
+is_pod_class(unique int id: @usertype ref);
+is_standard_layout_class(unique int id: @usertype ref);
+
+is_complete(unique int id: @usertype ref);
+
+is_class_template(unique int id: @usertype ref);
+class_instantiation(
+ int to: @usertype ref,
+ int from: @usertype ref
+);
+class_template_argument(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+class_template_argument_value(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_proxy_class_for(
+ unique int id: @usertype ref,
+ unique int templ_param_id: @usertype ref
+);
+
+type_mentions(
+ unique int id: @type_mention,
+ int type_id: @type ref,
+ int location: @location ref,
+ // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there.
+ int kind: int ref
+);
+
+is_function_template(unique int id: @function ref);
+function_instantiation(
+ unique int to: @function ref,
+ int from: @function ref
+);
+function_template_argument(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+function_template_argument_value(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_variable_template(unique int id: @variable ref);
+variable_instantiation(
+ unique int to: @variable ref,
+ int from: @variable ref
+);
+variable_template_argument(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+variable_template_argument_value(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+/*
+ Fixed point types
+ precision(1) = short, precision(2) = default, precision(3) = long
+ is_unsigned(1) = unsigned is_unsigned(2) = signed
+ is_fract_type(1) = declared with _Fract
+ saturating(1) = declared with _Sat
+*/
+/* TODO
+fixedpointtypes(
+ unique int id: @fixedpointtype,
+ int precision: int ref,
+ int is_unsigned: int ref,
+ int is_fract_type: int ref,
+ int saturating: int ref);
+*/
+
+routinetypes(
+ unique int id: @routinetype,
+ int return_type: @type ref
+);
+
+routinetypeargs(
+ int routine: @routinetype ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+ptrtomembers(
+ unique int id: @ptrtomember,
+ int type_id: @type ref,
+ int class_id: @type ref
+);
+
+/*
+ specifiers for types, functions, and variables
+
+ "public",
+ "protected",
+ "private",
+
+ "const",
+ "volatile",
+ "static",
+
+ "pure",
+ "virtual",
+ "sealed", // Microsoft
+ "__interface", // Microsoft
+ "inline",
+ "explicit",
+
+ "near", // near far extension
+ "far", // near far extension
+ "__ptr32", // Microsoft
+ "__ptr64", // Microsoft
+ "__sptr", // Microsoft
+ "__uptr", // Microsoft
+ "dllimport", // Microsoft
+ "dllexport", // Microsoft
+ "thread", // Microsoft
+ "naked", // Microsoft
+ "microsoft_inline", // Microsoft
+ "forceinline", // Microsoft
+ "selectany", // Microsoft
+ "nothrow", // Microsoft
+ "novtable", // Microsoft
+ "noreturn", // Microsoft
+ "noinline", // Microsoft
+ "noalias", // Microsoft
+ "restrict", // Microsoft
+*/
+
+specifiers(
+ unique int id: @specifier,
+ unique string str: string ref
+);
+
+typespecifiers(
+ int type_id: @type ref,
+ int spec_id: @specifier ref
+);
+
+funspecifiers(
+ int func_id: @function ref,
+ int spec_id: @specifier ref
+);
+
+varspecifiers(
+ int var_id: @accessible ref,
+ int spec_id: @specifier ref
+);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ string name: string ref,
+ string name_space: string ref,
+ int location: @location_default ref
+);
+
+case @attribute.kind of
+ 0 = @gnuattribute
+| 1 = @stdattribute
+| 2 = @declspec
+| 3 = @msattribute
+| 4 = @alignas
+// ... 5 @objc_propertyattribute deprecated
+;
+
+attribute_args(
+ unique int id: @attribute_arg,
+ int kind: int ref,
+ int attribute: @attribute ref,
+ int index: int ref,
+ int location: @location_default ref
+);
+
+case @attribute_arg.kind of
+ 0 = @attribute_arg_empty
+| 1 = @attribute_arg_token
+| 2 = @attribute_arg_constant
+| 3 = @attribute_arg_type
+;
+
+attribute_arg_value(
+ unique int arg: @attribute_arg ref,
+ string value: string ref
+);
+attribute_arg_type(
+ unique int arg: @attribute_arg ref,
+ int type_id: @type ref
+);
+attribute_arg_name(
+ unique int arg: @attribute_arg ref,
+ string name: string ref
+);
+
+typeattributes(
+ int type_id: @type ref,
+ int spec_id: @attribute ref
+);
+
+funcattributes(
+ int func_id: @function ref,
+ int spec_id: @attribute ref
+);
+
+varattributes(
+ int var_id: @accessible ref,
+ int spec_id: @attribute ref
+);
+
+stmtattributes(
+ int stmt_id: @stmt ref,
+ int spec_id: @attribute ref
+);
+
+@type = @builtintype
+ | @derivedtype
+ | @usertype
+ /* TODO | @fixedpointtype */
+ | @routinetype
+ | @ptrtomember
+ | @decltype;
+
+unspecifiedtype(
+ unique int type_id: @type ref,
+ int unspecified_type_id: @type ref
+);
+
+member(
+ int parent: @type ref,
+ int index: int ref,
+ int child: @member ref
+);
+
+@enclosingfunction_child = @usertype | @variable | @namespace
+
+enclosingfunction(
+ unique int child: @enclosingfunction_child ref,
+ int parent: @function ref
+);
+
+derivations(
+ unique int derivation: @derivation,
+ int sub: @type ref,
+ int index: int ref,
+ int super: @type ref,
+ int location: @location_default ref
+);
+
+derspecifiers(
+ int der_id: @derivation ref,
+ int spec_id: @specifier ref
+);
+
+/**
+ * Contains the byte offset of the base class subobject within the derived
+ * class. Only holds for non-virtual base classes, but see table
+ * `virtual_base_offsets` for offsets of virtual base class subobjects.
+ */
+direct_base_offsets(
+ unique int der_id: @derivation ref,
+ int offset: int ref
+);
+
+/**
+ * Contains the byte offset of the virtual base class subobject for class
+ * `super` within a most-derived object of class `sub`. `super` can be either a
+ * direct or indirect base class.
+ */
+#keyset[sub, super]
+virtual_base_offsets(
+ int sub: @usertype ref,
+ int super: @usertype ref,
+ int offset: int ref
+);
+
+frienddecls(
+ unique int id: @frienddecl,
+ int type_id: @type ref,
+ int decl_id: @declaration ref,
+ int location: @location_default ref
+);
+
+@declaredtype = @usertype ;
+
+@declaration = @function
+ | @declaredtype
+ | @variable
+ | @enumconstant
+ | @frienddecl;
+
+@member = @membervariable
+ | @function
+ | @declaredtype
+ | @enumconstant;
+
+@locatable = @diagnostic
+ | @declaration
+ | @ppd_include
+ | @ppd_define
+ | @macroinvocation
+ /*| @funcall*/
+ | @xmllocatable
+ | @attribute
+ | @attribute_arg;
+
+@namedscope = @namespace | @usertype;
+
+@element = @locatable
+ | @file
+ | @folder
+ | @specifier
+ | @type
+ | @expr
+ | @namespace
+ | @initialiser
+ | @stmt
+ | @derivation
+ | @comment
+ | @preprocdirect
+ | @fun_decl
+ | @var_decl
+ | @type_decl
+ | @namespace_decl
+ | @using
+ | @namequalifier
+ | @specialnamequalifyingelement
+ | @static_assert
+ | @type_mention
+ | @lambdacapture;
+
+@exprparent = @element;
+
+comments(
+ unique int id: @comment,
+ string contents: string ref,
+ int location: @location_default ref
+);
+
+commentbinding(
+ int id: @comment ref,
+ int element: @element ref
+);
+
+exprconv(
+ int converted: @expr ref,
+ unique int conversion: @expr ref
+);
+
+compgenerated(unique int id: @element ref);
+
+/**
+ * `destructor_call` destructs the `i`'th entity that should be
+ * destructed following `element`. Note that entities should be
+ * destructed in reverse construction order, so for a given `element`
+ * these should be called from highest to lowest `i`.
+ */
+#keyset[element, destructor_call]
+#keyset[element, i]
+synthetic_destructor_call(
+ int element: @element ref,
+ int i: int ref,
+ int destructor_call: @routineexpr ref
+);
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref
+);
+
+namespace_inline(
+ unique int id: @namespace ref
+);
+
+namespacembrs(
+ int parentid: @namespace ref,
+ unique int memberid: @namespacembr ref
+);
+
+@namespacembr = @declaration | @namespace;
+
+exprparents(
+ int expr_id: @expr ref,
+ int child_index: int ref,
+ int parent_id: @exprparent ref
+);
+
+expr_isload(unique int expr_id: @expr ref);
+
+@cast = @c_style_cast
+ | @const_cast
+ | @dynamic_cast
+ | @reinterpret_cast
+ | @static_cast
+ ;
+
+/*
+case @conversion.kind of
+ 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast
+| 1 = @bool_conversion // conversion to 'bool'
+| 2 = @base_class_conversion // a derived-to-base conversion
+| 3 = @derived_class_conversion // a base-to-derived conversion
+| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member
+| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member
+| 6 = @glvalue_adjust // an adjustment of the type of a glvalue
+| 7 = @prvalue_adjust // an adjustment of the type of a prvalue
+;
+*/
+/**
+ * Describes the semantics represented by a cast expression. This is largely
+ * independent of the source syntax of the cast, so it is separate from the
+ * regular expression kind.
+ */
+conversionkinds(
+ unique int expr_id: @cast ref,
+ int kind: int ref
+);
+
+@conversion = @cast
+ | @array_to_pointer
+ | @parexpr
+ | @reference_to
+ | @ref_indirect
+ | @temp_init
+ ;
+
+/*
+case @funbindexpr.kind of
+ 0 = @normal_call // a normal call
+| 1 = @virtual_call // a virtual call
+| 2 = @adl_call // a call whose target is only found by ADL
+;
+*/
+iscall(unique int caller: @funbindexpr ref, int kind: int ref);
+
+numtemplatearguments(
+ unique int expr_id: @expr ref,
+ int num: int ref
+);
+
+specialnamequalifyingelements(
+ unique int id: @specialnamequalifyingelement,
+ unique string name: string ref
+);
+
+@namequalifiableelement = @expr | @namequalifier;
+@namequalifyingelement = @namespace
+ | @specialnamequalifyingelement
+ | @usertype;
+
+namequalifiers(
+ unique int id: @namequalifier,
+ unique int qualifiableelement: @namequalifiableelement ref,
+ int qualifyingelement: @namequalifyingelement ref,
+ int location: @location_default ref
+);
+
+varbind(
+ int expr: @varbindexpr ref,
+ int var: @accessible ref
+);
+
+funbind(
+ int expr: @funbindexpr ref,
+ int fun: @function ref
+);
+
+@any_new_expr = @new_expr
+ | @new_array_expr;
+
+@new_or_delete_expr = @any_new_expr
+ | @delete_expr
+ | @delete_array_expr;
+
+@prefix_crement_expr = @preincrexpr | @predecrexpr;
+
+@postfix_crement_expr = @postincrexpr | @postdecrexpr;
+
+@increment_expr = @preincrexpr | @postincrexpr;
+
+@decrement_expr = @predecrexpr | @postdecrexpr;
+
+@crement_expr = @increment_expr | @decrement_expr;
+
+@un_arith_op_expr = @arithnegexpr
+ | @unaryplusexpr
+ | @conjugation
+ | @realpartexpr
+ | @imagpartexpr
+ | @crement_expr
+ ;
+
+@un_bitwise_op_expr = @complementexpr;
+
+@un_log_op_expr = @notexpr;
+
+@un_op_expr = @address_of
+ | @indirect
+ | @un_arith_op_expr
+ | @un_bitwise_op_expr
+ | @builtinaddressof
+ | @vec_fill
+ | @un_log_op_expr
+ | @co_await
+ | @co_yield
+ ;
+
+@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr;
+
+@cmp_op_expr = @eq_op_expr | @rel_op_expr;
+
+@eq_op_expr = @eqexpr | @neexpr;
+
+@rel_op_expr = @gtexpr
+ | @ltexpr
+ | @geexpr
+ | @leexpr
+ | @spaceshipexpr
+ ;
+
+@bin_bitwise_op_expr = @lshiftexpr
+ | @rshiftexpr
+ | @andexpr
+ | @orexpr
+ | @xorexpr
+ ;
+
+@p_arith_op_expr = @paddexpr
+ | @psubexpr
+ | @pdiffexpr
+ ;
+
+@bin_arith_op_expr = @addexpr
+ | @subexpr
+ | @mulexpr
+ | @divexpr
+ | @remexpr
+ | @jmulexpr
+ | @jdivexpr
+ | @fjaddexpr
+ | @jfaddexpr
+ | @fjsubexpr
+ | @jfsubexpr
+ | @minexpr
+ | @maxexpr
+ | @p_arith_op_expr
+ ;
+
+@bin_op_expr = @bin_arith_op_expr
+ | @bin_bitwise_op_expr
+ | @cmp_op_expr
+ | @bin_log_op_expr
+ ;
+
+@op_expr = @un_op_expr
+ | @bin_op_expr
+ | @assign_expr
+ | @conditionalexpr
+ ;
+
+@assign_arith_expr = @assignaddexpr
+ | @assignsubexpr
+ | @assignmulexpr
+ | @assigndivexpr
+ | @assignremexpr
+ ;
+
+@assign_bitwise_expr = @assignandexpr
+ | @assignorexpr
+ | @assignxorexpr
+ | @assignlshiftexpr
+ | @assignrshiftexpr
+ | @assignpaddexpr
+ | @assignpsubexpr
+ ;
+
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr
+
+@assign_expr = @assignexpr | @assign_op_expr
+
+/*
+ case @allocator.form of
+ 0 = plain
+ | 1 = alignment
+ ;
+*/
+
+/**
+ * The allocator function associated with a `new` or `new[]` expression.
+ * The `form` column specified whether the allocation call contains an alignment
+ * argument.
+ */
+expr_allocator(
+ unique int expr: @any_new_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/*
+ case @deallocator.form of
+ 0 = plain
+ | 1 = size
+ | 2 = alignment
+ | 3 = size_and_alignment
+ ;
+*/
+
+/**
+ * The deallocator function associated with a `delete`, `delete[]`, `new`, or
+ * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the
+ * one used to free memory if the initialization throws an exception.
+ * The `form` column specifies whether the deallocation call contains a size
+ * argument, and alignment argument, or both.
+ */
+expr_deallocator(
+ unique int expr: @new_or_delete_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/**
+ * Holds if the `@conditionalexpr` is of the two operand form
+ * `guard ? : false`.
+ */
+expr_cond_two_operand(
+ unique int cond: @conditionalexpr ref
+);
+
+/**
+ * The guard of `@conditionalexpr` `guard ? true : false`
+ */
+expr_cond_guard(
+ unique int cond: @conditionalexpr ref,
+ int guard: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` holds. For the two operand form
+ * `guard ?: false` consider using `expr_cond_guard` instead.
+ */
+expr_cond_true(
+ unique int cond: @conditionalexpr ref,
+ int true: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` does not hold.
+ */
+expr_cond_false(
+ unique int cond: @conditionalexpr ref,
+ int false: @expr ref
+);
+
+/** A string representation of the value. */
+values(
+ unique int id: @value,
+ string str: string ref
+);
+
+/** The actual text in the source code for the value, if any. */
+valuetext(
+ unique int id: @value ref,
+ string text: string ref
+);
+
+valuebind(
+ int val: @value ref,
+ unique int expr: @expr ref
+);
+
+fieldoffsets(
+ unique int id: @variable ref,
+ int byteoffset: int ref,
+ int bitoffset: int ref
+);
+
+bitfield(
+ unique int id: @variable ref,
+ int bits: int ref,
+ int declared_bits: int ref
+);
+
+/* TODO
+memberprefix(
+ int member: @expr ref,
+ int prefix: @expr ref
+);
+*/
+
+/*
+ kind(1) = mbrcallexpr
+ kind(2) = mbrptrcallexpr
+ kind(3) = mbrptrmbrcallexpr
+ kind(4) = ptrmbrptrmbrcallexpr
+ kind(5) = mbrreadexpr // x.y
+ kind(6) = mbrptrreadexpr // p->y
+ kind(7) = mbrptrmbrreadexpr // x.*pm
+ kind(8) = mbrptrmbrptrreadexpr // x->*pm
+ kind(9) = staticmbrreadexpr // static x.y
+ kind(10) = staticmbrptrreadexpr // static p->y
+*/
+/* TODO
+memberaccess(
+ int member: @expr ref,
+ int kind: int ref
+);
+*/
+
+initialisers(
+ unique int init: @initialiser,
+ int var: @accessible ref,
+ unique int expr: @expr ref,
+ int location: @location_expr ref
+);
+
+braced_initialisers(
+ int init: @initialiser ref
+);
+
+/**
+ * An ancestor for the expression, for cases in which we cannot
+ * otherwise find the expression's parent.
+ */
+expr_ancestor(
+ int exp: @expr ref,
+ int ancestor: @element ref
+);
+
+exprs(
+ unique int id: @expr,
+ int kind: int ref,
+ int location: @location_expr ref
+);
+
+/*
+ case @value.category of
+ 1 = prval
+ | 2 = xval
+ | 3 = lval
+ ;
+*/
+expr_types(
+ int id: @expr ref,
+ int typeid: @type ref,
+ int value_category: int ref
+);
+
+case @expr.kind of
+ 1 = @errorexpr
+| 2 = @address_of // & AddressOfExpr
+| 3 = @reference_to // ReferenceToExpr (implicit?)
+| 4 = @indirect // * PointerDereferenceExpr
+| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?)
+// ...
+| 8 = @array_to_pointer // (???)
+| 9 = @vacuous_destructor_call // VacuousDestructorCall
+// ...
+| 11 = @assume // Microsoft
+| 12 = @parexpr
+| 13 = @arithnegexpr
+| 14 = @unaryplusexpr
+| 15 = @complementexpr
+| 16 = @notexpr
+| 17 = @conjugation // GNU ~ operator
+| 18 = @realpartexpr // GNU __real
+| 19 = @imagpartexpr // GNU __imag
+| 20 = @postincrexpr
+| 21 = @postdecrexpr
+| 22 = @preincrexpr
+| 23 = @predecrexpr
+| 24 = @conditionalexpr
+| 25 = @addexpr
+| 26 = @subexpr
+| 27 = @mulexpr
+| 28 = @divexpr
+| 29 = @remexpr
+| 30 = @jmulexpr // C99 mul imaginary
+| 31 = @jdivexpr // C99 div imaginary
+| 32 = @fjaddexpr // C99 add real + imaginary
+| 33 = @jfaddexpr // C99 add imaginary + real
+| 34 = @fjsubexpr // C99 sub real - imaginary
+| 35 = @jfsubexpr // C99 sub imaginary - real
+| 36 = @paddexpr // pointer add (pointer + int or int + pointer)
+| 37 = @psubexpr // pointer sub (pointer - integer)
+| 38 = @pdiffexpr // difference between two pointers
+| 39 = @lshiftexpr
+| 40 = @rshiftexpr
+| 41 = @andexpr
+| 42 = @orexpr
+| 43 = @xorexpr
+| 44 = @eqexpr
+| 45 = @neexpr
+| 46 = @gtexpr
+| 47 = @ltexpr
+| 48 = @geexpr
+| 49 = @leexpr
+| 50 = @minexpr // GNU minimum
+| 51 = @maxexpr // GNU maximum
+| 52 = @assignexpr
+| 53 = @assignaddexpr
+| 54 = @assignsubexpr
+| 55 = @assignmulexpr
+| 56 = @assigndivexpr
+| 57 = @assignremexpr
+| 58 = @assignlshiftexpr
+| 59 = @assignrshiftexpr
+| 60 = @assignandexpr
+| 61 = @assignorexpr
+| 62 = @assignxorexpr
+| 63 = @assignpaddexpr // assign pointer add
+| 64 = @assignpsubexpr // assign pointer sub
+| 65 = @andlogicalexpr
+| 66 = @orlogicalexpr
+| 67 = @commaexpr
+| 68 = @subscriptexpr // access to member of an array, e.g., a[5]
+// ... 69 @objc_subscriptexpr deprecated
+// ... 70 @cmdaccess deprecated
+// ...
+| 73 = @virtfunptrexpr
+| 74 = @callexpr
+// ... 75 @msgexpr_normal deprecated
+// ... 76 @msgexpr_super deprecated
+// ... 77 @atselectorexpr deprecated
+// ... 78 @atprotocolexpr deprecated
+| 79 = @vastartexpr
+| 80 = @vaargexpr
+| 81 = @vaendexpr
+| 82 = @vacopyexpr
+// ... 83 @atencodeexpr deprecated
+| 84 = @varaccess
+| 85 = @thisaccess
+// ... 86 @objc_box_expr deprecated
+| 87 = @new_expr
+| 88 = @delete_expr
+| 89 = @throw_expr
+| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2)
+| 91 = @braced_init_list
+| 92 = @type_id
+| 93 = @runtime_sizeof
+| 94 = @runtime_alignof
+| 95 = @sizeof_pack
+| 96 = @expr_stmt // GNU extension
+| 97 = @routineexpr
+| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....)
+| 99 = @offsetofexpr // offsetof ::= type and field
+| 100 = @hasassignexpr // __has_assign ::= type
+| 101 = @hascopyexpr // __has_copy ::= type
+| 102 = @hasnothrowassign // __has_nothrow_assign ::= type
+| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type
+| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type
+| 105 = @hastrivialassign // __has_trivial_assign ::= type
+| 106 = @hastrivialconstr // __has_trivial_constructor ::= type
+| 107 = @hastrivialcopy // __has_trivial_copy ::= type
+| 108 = @hasuserdestr // __has_user_destructor ::= type
+| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type
+| 110 = @isabstractexpr // __is_abstract ::= type
+| 111 = @isbaseofexpr // __is_base_of ::= type type
+| 112 = @isclassexpr // __is_class ::= type
+| 113 = @isconvtoexpr // __is_convertible_to ::= type type
+| 114 = @isemptyexpr // __is_empty ::= type
+| 115 = @isenumexpr // __is_enum ::= type
+| 116 = @ispodexpr // __is_pod ::= type
+| 117 = @ispolyexpr // __is_polymorphic ::= type
+| 118 = @isunionexpr // __is_union ::= type
+| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type
+| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof
+// ...
+| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type
+| 123 = @literal
+| 124 = @uuidof
+| 127 = @aggregateliteral
+| 128 = @delete_array_expr
+| 129 = @new_array_expr
+// ... 130 @objc_array_literal deprecated
+// ... 131 @objc_dictionary_literal deprecated
+| 132 = @foldexpr
+// ...
+| 200 = @ctordirectinit
+| 201 = @ctorvirtualinit
+| 202 = @ctorfieldinit
+| 203 = @ctordelegatinginit
+| 204 = @dtordirectdestruct
+| 205 = @dtorvirtualdestruct
+| 206 = @dtorfielddestruct
+// ...
+| 210 = @static_cast
+| 211 = @reinterpret_cast
+| 212 = @const_cast
+| 213 = @dynamic_cast
+| 214 = @c_style_cast
+| 215 = @lambdaexpr
+| 216 = @param_ref
+| 217 = @noopexpr
+// ...
+| 294 = @istriviallyconstructibleexpr
+| 295 = @isdestructibleexpr
+| 296 = @isnothrowdestructibleexpr
+| 297 = @istriviallydestructibleexpr
+| 298 = @istriviallyassignableexpr
+| 299 = @isnothrowassignableexpr
+| 300 = @istrivialexpr
+| 301 = @isstandardlayoutexpr
+| 302 = @istriviallycopyableexpr
+| 303 = @isliteraltypeexpr
+| 304 = @hastrivialmoveconstructorexpr
+| 305 = @hastrivialmoveassignexpr
+| 306 = @hasnothrowmoveassignexpr
+| 307 = @isconstructibleexpr
+| 308 = @isnothrowconstructibleexpr
+| 309 = @hasfinalizerexpr
+| 310 = @isdelegateexpr
+| 311 = @isinterfaceclassexpr
+| 312 = @isrefarrayexpr
+| 313 = @isrefclassexpr
+| 314 = @issealedexpr
+| 315 = @issimplevalueclassexpr
+| 316 = @isvalueclassexpr
+| 317 = @isfinalexpr
+| 319 = @noexceptexpr
+| 320 = @builtinshufflevector
+| 321 = @builtinchooseexpr
+| 322 = @builtinaddressof
+| 323 = @vec_fill
+| 324 = @builtinconvertvector
+| 325 = @builtincomplex
+| 326 = @spaceshipexpr
+| 327 = @co_await
+| 328 = @co_yield
+| 329 = @temp_init
+| 330 = @isassignable
+| 331 = @isaggregate
+| 332 = @hasuniqueobjectrepresentations
+| 333 = @builtinbitcast
+| 334 = @builtinshuffle
+;
+
+@var_args_expr = @vastartexpr
+ | @vaendexpr
+ | @vaargexpr
+ | @vacopyexpr
+ ;
+
+@builtin_op = @var_args_expr
+ | @noopexpr
+ | @offsetofexpr
+ | @intaddrexpr
+ | @hasassignexpr
+ | @hascopyexpr
+ | @hasnothrowassign
+ | @hasnothrowconstr
+ | @hasnothrowcopy
+ | @hastrivialassign
+ | @hastrivialconstr
+ | @hastrivialcopy
+ | @hastrivialdestructor
+ | @hasuserdestr
+ | @hasvirtualdestr
+ | @isabstractexpr
+ | @isbaseofexpr
+ | @isclassexpr
+ | @isconvtoexpr
+ | @isemptyexpr
+ | @isenumexpr
+ | @ispodexpr
+ | @ispolyexpr
+ | @isunionexpr
+ | @typescompexpr
+ | @builtinshufflevector
+ | @builtinconvertvector
+ | @builtinaddressof
+ | @istriviallyconstructibleexpr
+ | @isdestructibleexpr
+ | @isnothrowdestructibleexpr
+ | @istriviallydestructibleexpr
+ | @istriviallyassignableexpr
+ | @isnothrowassignableexpr
+ | @isstandardlayoutexpr
+ | @istriviallycopyableexpr
+ | @isliteraltypeexpr
+ | @hastrivialmoveconstructorexpr
+ | @hastrivialmoveassignexpr
+ | @hasnothrowmoveassignexpr
+ | @isconstructibleexpr
+ | @isnothrowconstructibleexpr
+ | @hasfinalizerexpr
+ | @isdelegateexpr
+ | @isinterfaceclassexpr
+ | @isrefarrayexpr
+ | @isrefclassexpr
+ | @issealedexpr
+ | @issimplevalueclassexpr
+ | @isvalueclassexpr
+ | @isfinalexpr
+ | @builtinchooseexpr
+ | @builtincomplex
+ | @isassignable
+ | @isaggregate
+ | @hasuniqueobjectrepresentations
+ | @builtinbitcast
+ | @builtinshuffle
+ ;
+
+new_allocated_type(
+ unique int expr: @new_expr ref,
+ int type_id: @type ref
+);
+
+new_array_allocated_type(
+ unique int expr: @new_array_expr ref,
+ int type_id: @type ref
+);
+
+/**
+ * The field being initialized by an initializer expression within an aggregate
+ * initializer for a class/struct/union.
+ */
+#keyset[aggregate, field]
+aggregate_field_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int field: @membervariable ref
+);
+
+/**
+ * The index of the element being initialized by an initializer expression
+ * within an aggregate initializer for an array.
+ */
+#keyset[aggregate, element_index]
+aggregate_array_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int element_index: int ref
+);
+
+@ctorinit = @ctordirectinit
+ | @ctorvirtualinit
+ | @ctorfieldinit
+ | @ctordelegatinginit;
+@dtordestruct = @dtordirectdestruct
+ | @dtorvirtualdestruct
+ | @dtorfielddestruct;
+
+
+condition_decl_bind(
+ unique int expr: @condition_decl ref,
+ unique int decl: @declaration ref
+);
+
+typeid_bind(
+ unique int expr: @type_id ref,
+ int type_id: @type ref
+);
+
+uuidof_bind(
+ unique int expr: @uuidof ref,
+ int type_id: @type ref
+);
+
+@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof;
+
+sizeof_bind(
+ unique int expr: @runtime_sizeof_or_alignof ref,
+ int type_id: @type ref
+);
+
+code_block(
+ unique int block: @literal ref,
+ unique int routine: @function ref
+);
+
+lambdas(
+ unique int expr: @lambdaexpr ref,
+ string default_capture: string ref,
+ boolean has_explicit_return_type: boolean ref
+);
+
+lambda_capture(
+ unique int id: @lambdacapture,
+ int lambda: @lambdaexpr ref,
+ int index: int ref,
+ int field: @membervariable ref,
+ boolean captured_by_reference: boolean ref,
+ boolean is_implicit: boolean ref,
+ int location: @location_default ref
+);
+
+@funbindexpr = @routineexpr
+ | @new_expr
+ | @delete_expr
+ | @delete_array_expr
+ | @ctordirectinit
+ | @ctorvirtualinit
+ | @ctordelegatinginit
+ | @dtordirectdestruct
+ | @dtorvirtualdestruct;
+
+@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct;
+@addressable = @function | @variable ;
+@accessible = @addressable | @enumconstant ;
+
+@access = @varaccess | @routineexpr ;
+
+fold(
+ int expr: @foldexpr ref,
+ string operator: string ref,
+ boolean is_left_fold: boolean ref
+);
+
+stmts(
+ unique int id: @stmt,
+ int kind: int ref,
+ int location: @location_stmt ref
+);
+
+case @stmt.kind of
+ 1 = @stmt_expr
+| 2 = @stmt_if
+| 3 = @stmt_while
+| 4 = @stmt_goto
+| 5 = @stmt_label
+| 6 = @stmt_return
+| 7 = @stmt_block
+| 8 = @stmt_end_test_while // do { ... } while ( ... )
+| 9 = @stmt_for
+| 10 = @stmt_switch_case
+| 11 = @stmt_switch
+| 13 = @stmt_asm // "asm" statement or the body of an asm function
+| 15 = @stmt_try_block
+| 16 = @stmt_microsoft_try // Microsoft
+| 17 = @stmt_decl
+| 18 = @stmt_set_vla_size // C99
+| 19 = @stmt_vla_decl // C99
+| 25 = @stmt_assigned_goto // GNU
+| 26 = @stmt_empty
+| 27 = @stmt_continue
+| 28 = @stmt_break
+| 29 = @stmt_range_based_for // C++11
+// ... 30 @stmt_at_autoreleasepool_block deprecated
+// ... 31 @stmt_objc_for_in deprecated
+// ... 32 @stmt_at_synchronized deprecated
+| 33 = @stmt_handler
+// ... 34 @stmt_finally_end deprecated
+| 35 = @stmt_constexpr_if
+| 37 = @stmt_co_return
+;
+
+type_vla(
+ int type_id: @type ref,
+ int decl: @stmt_vla_decl ref
+);
+
+variable_vla(
+ int var: @variable ref,
+ int decl: @stmt_vla_decl ref
+);
+
+if_initialization(
+ unique int if_stmt: @stmt_if ref,
+ int init_id: @stmt ref
+);
+
+if_then(
+ unique int if_stmt: @stmt_if ref,
+ int then_id: @stmt ref
+);
+
+if_else(
+ unique int if_stmt: @stmt_if ref,
+ int else_id: @stmt ref
+);
+
+constexpr_if_initialization(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int init_id: @stmt ref
+);
+
+constexpr_if_then(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int then_id: @stmt ref
+);
+
+constexpr_if_else(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int else_id: @stmt ref
+);
+
+while_body(
+ unique int while_stmt: @stmt_while ref,
+ int body_id: @stmt ref
+);
+
+do_body(
+ unique int do_stmt: @stmt_end_test_while ref,
+ int body_id: @stmt ref
+);
+
+switch_initialization(
+ unique int switch_stmt: @stmt_switch ref,
+ int init_id: @stmt ref
+);
+
+#keyset[switch_stmt, index]
+switch_case(
+ int switch_stmt: @stmt_switch ref,
+ int index: int ref,
+ int case_id: @stmt_switch_case ref
+);
+
+switch_body(
+ unique int switch_stmt: @stmt_switch ref,
+ int body_id: @stmt ref
+);
+
+for_initialization(
+ unique int for_stmt: @stmt_for ref,
+ int init_id: @stmt ref
+);
+
+for_condition(
+ unique int for_stmt: @stmt_for ref,
+ int condition_id: @expr ref
+);
+
+for_update(
+ unique int for_stmt: @stmt_for ref,
+ int update_id: @expr ref
+);
+
+for_body(
+ unique int for_stmt: @stmt_for ref,
+ int body_id: @stmt ref
+);
+
+@stmtparent = @stmt | @expr_stmt ;
+stmtparents(
+ unique int id: @stmt ref,
+ int index: int ref,
+ int parent: @stmtparent ref
+);
+
+ishandler(unique int block: @stmt_block ref);
+
+@cfgnode = @stmt | @expr | @function | @initialiser ;
+
+stmt_decl_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl: @declaration ref
+);
+
+stmt_decl_entry_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl_entry: @element ref
+);
+
+@functionorblock = @function | @stmt_block;
+
+blockscope(
+ unique int block: @stmt_block ref,
+ int enclosing: @functionorblock ref
+);
+
+@jump = @stmt_goto | @stmt_break | @stmt_continue;
+
+@jumporlabel = @jump | @stmt_label | @literal;
+
+jumpinfo(
+ unique int id: @jumporlabel ref,
+ string str: string ref,
+ int target: @stmt ref
+);
+
+preprocdirects(
+ unique int id: @preprocdirect,
+ int kind: int ref,
+ int location: @location_default ref
+);
+case @preprocdirect.kind of
+ 0 = @ppd_if
+| 1 = @ppd_ifdef
+| 2 = @ppd_ifndef
+| 3 = @ppd_elif
+| 4 = @ppd_else
+| 5 = @ppd_endif
+| 6 = @ppd_plain_include
+| 7 = @ppd_define
+| 8 = @ppd_undef
+| 9 = @ppd_line
+| 10 = @ppd_error
+| 11 = @ppd_pragma
+| 12 = @ppd_objc_import
+| 13 = @ppd_include_next
+| 18 = @ppd_warning
+;
+
+@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next;
+
+@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif;
+
+preprocpair(
+ int begin : @ppd_branch ref,
+ int elseelifend : @preprocdirect ref
+);
+
+preproctrue(int branch : @ppd_branch ref);
+preprocfalse(int branch : @ppd_branch ref);
+
+preproctext(
+ unique int id: @preprocdirect ref,
+ string head: string ref,
+ string body: string ref
+);
+
+includes(
+ unique int id: @ppd_include ref,
+ int included: @file ref
+);
+
+link_targets(
+ unique int id: @link_target,
+ int binary: @file ref
+);
+
+link_parent(
+ int element : @element ref,
+ int link_target : @link_target ref
+);
+
+/* XML Files */
+
+xmlEncoding(unique int id: @file ref, string encoding: string ref);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ string root: string ref,
+ string publicId: string ref,
+ string systemId: string ref,
+ int fileid: @file ref
+);
+
+xmlElements(
+ unique int id: @xmlelement,
+ string name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ string name: string ref,
+ string value: string ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlNs(
+ int id: @xmlnamespace,
+ string prefixName: string ref,
+ string URI: string ref,
+ int fileid: @file ref
+);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref
+);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref
+);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref
+);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref
+);
+
+@xmllocatable = @xmlcharacters
+ | @xmlelement
+ | @xmlcomment
+ | @xmlattribute
+ | @xmldtd
+ | @file
+ | @xmlnamespace;
diff --git a/cpp/ql/lib/upgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/semmlecode.cpp.dbscheme b/cpp/ql/lib/upgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/semmlecode.cpp.dbscheme
new file mode 100644
index 00000000000..34549c3b093
--- /dev/null
+++ b/cpp/ql/lib/upgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/semmlecode.cpp.dbscheme
@@ -0,0 +1,2130 @@
+
+/**
+ * An invocation of the compiler. Note that more than one file may be
+ * compiled per invocation. For example, this command compiles three
+ * source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * The `id` simply identifies the invocation, while `cwd` is the working
+ * directory from which the compiler was invoked.
+ */
+compilations(
+ /**
+ * An invocation of the compiler. Note that more than one file may
+ * be compiled per invocation. For example, this command compiles
+ * three source files:
+ *
+ * gcc -c f1.c f2.c f3.c
+ */
+ unique int id : @compilation,
+ string cwd : string ref
+);
+
+/**
+ * The arguments that were passed to the extractor for a compiler
+ * invocation. If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then typically there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | *path to extractor*
+ * 1 | `--mimic`
+ * 2 | `/usr/bin/gcc`
+ * 3 | `-c`
+ * 4 | f1.c
+ * 5 | f2.c
+ * 6 | f3.c
+ */
+#keyset[id, num]
+compilation_args(
+ int id : @compilation ref,
+ int num : int ref,
+ string arg : string ref
+);
+
+/**
+ * The source files that are compiled by a compiler invocation.
+ * If `id` is for the compiler invocation
+ *
+ * gcc -c f1.c f2.c f3.c
+ *
+ * then there will be rows for
+ *
+ * num | arg
+ * --- | ---
+ * 0 | f1.c
+ * 1 | f2.c
+ * 2 | f3.c
+ *
+ * Note that even if those files `#include` headers, those headers
+ * do not appear as rows.
+ */
+#keyset[id, num]
+compilation_compiling_files(
+ int id : @compilation ref,
+ int num : int ref,
+ int file : @file ref
+);
+
+/**
+ * The time taken by the extractor for a compiler invocation.
+ *
+ * For each file `num`, there will be rows for
+ *
+ * kind | seconds
+ * ---- | ---
+ * 1 | CPU seconds used by the extractor frontend
+ * 2 | Elapsed seconds during the extractor frontend
+ * 3 | CPU seconds used by the extractor backend
+ * 4 | Elapsed seconds during the extractor backend
+ */
+#keyset[id, num, kind]
+compilation_time(
+ int id : @compilation ref,
+ int num : int ref,
+ /* kind:
+ 1 = frontend_cpu_seconds
+ 2 = frontend_elapsed_seconds
+ 3 = extractor_cpu_seconds
+ 4 = extractor_elapsed_seconds
+ */
+ int kind : int ref,
+ float seconds : float ref
+);
+
+/**
+ * An error or warning generated by the extractor.
+ * The diagnostic message `diagnostic` was generated during compiler
+ * invocation `compilation`, and is the `file_number_diagnostic_number`th
+ * message generated while extracting the `file_number`th file of that
+ * invocation.
+ */
+#keyset[compilation, file_number, file_number_diagnostic_number]
+diagnostic_for(
+ int diagnostic : @diagnostic ref,
+ int compilation : @compilation ref,
+ int file_number : int ref,
+ int file_number_diagnostic_number : int ref
+);
+
+/**
+ * If extraction was successful, then `cpu_seconds` and
+ * `elapsed_seconds` are the CPU time and elapsed time (respectively)
+ * that extraction took for compiler invocation `id`.
+ */
+compilation_finished(
+ unique int id : @compilation ref,
+ float cpu_seconds : float ref,
+ float elapsed_seconds : float ref
+);
+
+
+/**
+ * External data, loaded from CSV files during snapshot creation. See
+ * [Tutorial: Incorporating external data](https://help.semmle.com/wiki/display/SD/Tutorial%3A+Incorporating+external+data)
+ * for more information.
+ */
+externalData(
+ int id : @externalDataElement,
+ string path : string ref,
+ int column: int ref,
+ string value : string ref
+);
+
+/**
+ * The source location of the snapshot.
+ */
+sourceLocationPrefix(string prefix : string ref);
+
+/**
+ * Information about packages that provide code used during compilation.
+ * The `id` is just a unique identifier.
+ * The `namespace` is typically the name of the package manager that
+ * provided the package (e.g. "dpkg" or "yum").
+ * The `package_name` is the name of the package, and `version` is its
+ * version (as a string).
+ */
+external_packages(
+ unique int id: @external_package,
+ string namespace : string ref,
+ string package_name : string ref,
+ string version : string ref
+);
+
+/**
+ * Holds if File `fileid` was provided by package `package`.
+ */
+header_to_external_package(
+ int fileid : @file ref,
+ int package : @external_package ref
+);
+
+/*
+ * Version history
+ */
+
+svnentries(
+ unique int id : @svnentry,
+ string revision : string ref,
+ string author : string ref,
+ date revisionDate : date ref,
+ int changeSize : int ref
+)
+
+svnaffectedfiles(
+ int id : @svnentry ref,
+ int file : @file ref,
+ string action : string ref
+)
+
+svnentrymsg(
+ unique int id : @svnentry ref,
+ string message : string ref
+)
+
+svnchurn(
+ int commit : @svnentry ref,
+ int file : @file ref,
+ int addedLines : int ref,
+ int deletedLines : int ref
+)
+
+/*
+ * C++ dbscheme
+ */
+
+@location = @location_stmt | @location_expr | @location_default ;
+
+/**
+ * The location of an element that is not an expression or a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_default(
+ /** The location of an element that is not an expression or a statement. */
+ unique int id: @location_default,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of a statement.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_stmt(
+ /** The location of a statement. */
+ unique int id: @location_stmt,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/**
+ * The location of an expression.
+ * The location spans column `startcolumn` of line `startline` to
+ * column `endcolumn` of line `endline` in file `file`.
+ * For more information, see
+ * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/).
+ */
+locations_expr(
+ /** The location of an expression. */
+ unique int id: @location_expr,
+ int container: @container ref,
+ int startLine: int ref,
+ int startColumn: int ref,
+ int endLine: int ref,
+ int endColumn: int ref
+);
+
+/** An element for which line-count information is available. */
+@sourceline = @file | @function | @variable | @enumconstant | @xmllocatable;
+
+numlines(
+ int element_id: @sourceline ref,
+ int num_lines: int ref,
+ int num_code: int ref,
+ int num_comment: int ref
+);
+
+diagnostics(
+ unique int id: @diagnostic,
+ int severity: int ref,
+ string error_tag: string ref,
+ string error_message: string ref,
+ string full_error_message: string ref,
+ int location: @location_default ref
+);
+
+files(
+ unique int id: @file,
+ string name: string ref
+);
+
+folders(
+ unique int id: @folder,
+ string name: string ref
+);
+
+@container = @folder | @file
+
+containerparent(
+ int parent: @container ref,
+ unique int child: @container ref
+);
+
+fileannotations(
+ int id: @file ref,
+ int kind: int ref,
+ string name: string ref,
+ string value: string ref
+);
+
+inmacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+affectedbymacroexpansion(
+ int id: @element ref,
+ int inv: @macroinvocation ref
+);
+
+/*
+ case @macroinvocations.kind of
+ 1 = macro expansion
+ | 2 = other macro reference
+ ;
+*/
+macroinvocations(
+ unique int id: @macroinvocation,
+ int macro_id: @ppd_define ref,
+ int location: @location_default ref,
+ int kind: int ref
+);
+
+macroparent(
+ unique int id: @macroinvocation ref,
+ int parent_id: @macroinvocation ref
+);
+
+// a macroinvocation may be part of another location
+// the way to find a constant expression that uses a macro
+// is thus to find a constant expression that has a location
+// to which a macro invocation is bound
+macrolocationbind(
+ int id: @macroinvocation ref,
+ int location: @location ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_unexpanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+#keyset[invocation, argument_index]
+macro_argument_expanded(
+ int invocation: @macroinvocation ref,
+ int argument_index: int ref,
+ string text: string ref
+);
+
+/*
+ case @function.kind of
+ 1 = normal
+ | 2 = constructor
+ | 3 = destructor
+ | 4 = conversion
+ | 5 = operator
+ | 6 = builtin // GCC built-in functions, e.g. __builtin___memcpy_chk
+ ;
+*/
+functions(
+ unique int id: @function,
+ string name: string ref,
+ int kind: int ref
+);
+
+function_entry_point(int id: @function ref, unique int entry_point: @stmt ref);
+
+function_return_type(int id: @function ref, int return_type: @type ref);
+
+/** If `function` is a coroutine, then this gives the
+ std::experimental::resumable_traits instance associated with it,
+ and the variables representing the `handle` and `promise` for it. */
+coroutine(
+ unique int function: @function ref,
+ int traits: @type ref,
+ int handle: @variable ref,
+ int promise: @variable ref
+);
+
+/** The `new` function used for allocating the coroutine state, if any. */
+coroutine_new(
+ unique int function: @function ref,
+ int new: @function ref
+);
+
+/** The `delete` function used for deallocating the coroutine state, if any. */
+coroutine_delete(
+ unique int function: @function ref,
+ int delete: @function ref
+);
+
+purefunctions(unique int id: @function ref);
+
+function_deleted(unique int id: @function ref);
+
+function_defaulted(unique int id: @function ref);
+
+member_function_this_type(unique int id: @function ref, int this_type: @type ref);
+
+#keyset[id, type_id]
+fun_decls(
+ int id: @fun_decl,
+ int function: @function ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+fun_def(unique int id: @fun_decl ref);
+fun_specialized(unique int id: @fun_decl ref);
+fun_implicit(unique int id: @fun_decl ref);
+fun_decl_specifiers(
+ int id: @fun_decl ref,
+ string name: string ref
+)
+#keyset[fun_decl, index]
+fun_decl_throws(
+ int fun_decl: @fun_decl ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+/* an empty throw specification is different from none */
+fun_decl_empty_throws(unique int fun_decl: @fun_decl ref);
+fun_decl_noexcept(
+ int fun_decl: @fun_decl ref,
+ int constant: @expr ref
+);
+fun_decl_empty_noexcept(int fun_decl: @fun_decl ref);
+fun_decl_typedef_type(
+ unique int fun_decl: @fun_decl ref,
+ int typedeftype_id: @usertype ref
+);
+
+param_decl_bind(
+ unique int id: @var_decl ref,
+ int index: int ref,
+ int fun_decl: @fun_decl ref
+);
+
+#keyset[id, type_id]
+var_decls(
+ int id: @var_decl,
+ int variable: @variable ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+var_def(unique int id: @var_decl ref);
+var_decl_specifiers(
+ int id: @var_decl ref,
+ string name: string ref
+)
+is_structured_binding(unique int id: @variable ref);
+
+type_decls(
+ unique int id: @type_decl,
+ int type_id: @type ref,
+ int location: @location_default ref
+);
+type_def(unique int id: @type_decl ref);
+type_decl_top(
+ unique int type_decl: @type_decl ref
+);
+
+namespace_decls(
+ unique int id: @namespace_decl,
+ int namespace_id: @namespace ref,
+ int location: @location_default ref,
+ int bodylocation: @location_default ref
+);
+
+usings(
+ unique int id: @using,
+ int element_id: @element ref,
+ int location: @location_default ref
+);
+
+/** The element which contains the `using` declaration. */
+using_container(
+ int parent: @element ref,
+ int child: @using ref
+);
+
+static_asserts(
+ unique int id: @static_assert,
+ int condition : @expr ref,
+ string message : string ref,
+ int location: @location_default ref,
+ int enclosing : @element ref
+);
+
+// each function has an ordered list of parameters
+#keyset[id, type_id]
+#keyset[function, index, type_id]
+params(
+ int id: @parameter,
+ int function: @functionorblock ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+overrides(int new: @function ref, int old: @function ref);
+
+#keyset[id, type_id]
+membervariables(
+ int id: @membervariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+globalvariables(
+ int id: @globalvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+#keyset[id, type_id]
+localvariables(
+ int id: @localvariable,
+ int type_id: @type ref,
+ string name: string ref
+);
+
+autoderivation(
+ unique int var: @variable ref,
+ int derivation_type: @type ref
+);
+
+enumconstants(
+ unique int id: @enumconstant,
+ int parent: @usertype ref,
+ int index: int ref,
+ int type_id: @type ref,
+ string name: string ref,
+ int location: @location_default ref
+);
+
+@variable = @localscopevariable | @globalvariable | @membervariable;
+
+@localscopevariable = @localvariable | @parameter;
+
+/*
+ Built-in types are the fundamental types, e.g., integral, floating, and void.
+
+ case @builtintype.kind of
+ 1 = error
+ | 2 = unknown
+ | 3 = void
+ | 4 = boolean
+ | 5 = char
+ | 6 = unsigned_char
+ | 7 = signed_char
+ | 8 = short
+ | 9 = unsigned_short
+ | 10 = signed_short
+ | 11 = int
+ | 12 = unsigned_int
+ | 13 = signed_int
+ | 14 = long
+ | 15 = unsigned_long
+ | 16 = signed_long
+ | 17 = long_long
+ | 18 = unsigned_long_long
+ | 19 = signed_long_long
+ | 20 = __int8 // Microsoft-specific
+ | 21 = __int16 // Microsoft-specific
+ | 22 = __int32 // Microsoft-specific
+ | 23 = __int64 // Microsoft-specific
+ | 24 = float
+ | 25 = double
+ | 26 = long_double
+ | 27 = _Complex_float // C99-specific
+ | 28 = _Complex_double // C99-specific
+ | 29 = _Complex_long double // C99-specific
+ | 30 = _Imaginary_float // C99-specific
+ | 31 = _Imaginary_double // C99-specific
+ | 32 = _Imaginary_long_double // C99-specific
+ | 33 = wchar_t // Microsoft-specific
+ | 34 = decltype_nullptr // C++11
+ | 35 = __int128
+ | 36 = unsigned___int128
+ | 37 = signed___int128
+ | 38 = __float128
+ | 39 = _Complex___float128
+ | 40 = _Decimal32
+ | 41 = _Decimal64
+ | 42 = _Decimal128
+ | 43 = char16_t
+ | 44 = char32_t
+ | 45 = _Float32
+ | 46 = _Float32x
+ | 47 = _Float64
+ | 48 = _Float64x
+ | 49 = _Float128
+ | 50 = _Float128x
+ | 51 = char8_t
+ ;
+*/
+builtintypes(
+ unique int id: @builtintype,
+ string name: string ref,
+ int kind: int ref,
+ int size: int ref,
+ int sign: int ref,
+ int alignment: int ref
+);
+
+/*
+ Derived types are types that are directly derived from existing types and
+ point to, refer to, transform type data to return a new type.
+
+ case @derivedtype.kind of
+ 1 = pointer
+ | 2 = reference
+ | 3 = type_with_specifiers
+ | 4 = array
+ | 5 = gnu_vector
+ | 6 = routineptr
+ | 7 = routinereference
+ | 8 = rvalue_reference // C++11
+// ... 9 type_conforming_to_protocols deprecated
+ | 10 = block
+ ;
+*/
+derivedtypes(
+ unique int id: @derivedtype,
+ string name: string ref,
+ int kind: int ref,
+ int type_id: @type ref
+);
+
+pointerishsize(unique int id: @derivedtype ref,
+ int size: int ref,
+ int alignment: int ref);
+
+arraysizes(
+ unique int id: @derivedtype ref,
+ int num_elements: int ref,
+ int bytesize: int ref,
+ int alignment: int ref
+);
+
+typedefbase(
+ unique int id: @usertype ref,
+ int type_id: @type ref
+);
+
+/**
+ * An instance of the C++11 `decltype` operator. For example:
+ * ```
+ * int a;
+ * decltype(1+a) b;
+ * ```
+ * Here `expr` is `1+a`.
+ *
+ * Sometimes an additional pair of parentheses around the expression
+ * would change the semantics of this decltype, e.g.
+ * ```
+ * struct A { double x; };
+ * const A* a = new A();
+ * decltype( a->x ); // type is double
+ * decltype((a->x)); // type is const double&
+ * ```
+ * (Please consult the C++11 standard for more details).
+ * `parentheses_would_change_meaning` is `true` iff that is the case.
+ */
+#keyset[id, expr]
+decltypes(
+ int id: @decltype,
+ int expr: @expr ref,
+ int base_type: @type ref,
+ boolean parentheses_would_change_meaning: boolean ref
+);
+
+/*
+ case @usertype.kind of
+ 1 = struct
+ | 2 = class
+ | 3 = union
+ | 4 = enum
+ | 5 = typedef // classic C: typedef typedef type name
+ | 6 = template
+ | 7 = template_parameter
+ | 8 = template_template_parameter
+ | 9 = proxy_class // a proxy class associated with a template parameter
+// ... 10 objc_class deprecated
+// ... 11 objc_protocol deprecated
+// ... 12 objc_category deprecated
+ | 13 = scoped_enum
+ | 14 = using_alias // a using name = type style typedef
+ ;
+*/
+usertypes(
+ unique int id: @usertype,
+ string name: string ref,
+ int kind: int ref
+);
+
+usertypesize(
+ unique int id: @usertype ref,
+ int size: int ref,
+ int alignment: int ref
+);
+
+usertype_final(unique int id: @usertype ref);
+
+usertype_uuid(
+ unique int id: @usertype ref,
+ string uuid: string ref
+);
+
+mangled_name(
+ unique int id: @declaration ref,
+ int mangled_name : @mangledname
+);
+
+is_pod_class(unique int id: @usertype ref);
+is_standard_layout_class(unique int id: @usertype ref);
+
+is_complete(unique int id: @usertype ref);
+
+is_class_template(unique int id: @usertype ref);
+class_instantiation(
+ int to: @usertype ref,
+ int from: @usertype ref
+);
+class_template_argument(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+class_template_argument_value(
+ int type_id: @usertype ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_proxy_class_for(
+ unique int id: @usertype ref,
+ unique int templ_param_id: @usertype ref
+);
+
+type_mentions(
+ unique int id: @type_mention,
+ int type_id: @type ref,
+ int location: @location ref,
+ // a_symbol_reference_kind from the EDG frontend. See symbol_ref.h there.
+ int kind: int ref
+);
+
+is_function_template(unique int id: @function ref);
+function_instantiation(
+ unique int to: @function ref,
+ int from: @function ref
+);
+function_template_argument(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+function_template_argument_value(
+ int function_id: @function ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+is_variable_template(unique int id: @variable ref);
+variable_instantiation(
+ unique int to: @variable ref,
+ int from: @variable ref
+);
+variable_template_argument(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_type: @type ref
+);
+variable_template_argument_value(
+ int variable_id: @variable ref,
+ int index: int ref,
+ int arg_value: @expr ref
+);
+
+/*
+ Fixed point types
+ precision(1) = short, precision(2) = default, precision(3) = long
+ is_unsigned(1) = unsigned is_unsigned(2) = signed
+ is_fract_type(1) = declared with _Fract
+ saturating(1) = declared with _Sat
+*/
+/* TODO
+fixedpointtypes(
+ unique int id: @fixedpointtype,
+ int precision: int ref,
+ int is_unsigned: int ref,
+ int is_fract_type: int ref,
+ int saturating: int ref);
+*/
+
+routinetypes(
+ unique int id: @routinetype,
+ int return_type: @type ref
+);
+
+routinetypeargs(
+ int routine: @routinetype ref,
+ int index: int ref,
+ int type_id: @type ref
+);
+
+ptrtomembers(
+ unique int id: @ptrtomember,
+ int type_id: @type ref,
+ int class_id: @type ref
+);
+
+/*
+ specifiers for types, functions, and variables
+
+ "public",
+ "protected",
+ "private",
+
+ "const",
+ "volatile",
+ "static",
+
+ "pure",
+ "virtual",
+ "sealed", // Microsoft
+ "__interface", // Microsoft
+ "inline",
+ "explicit",
+
+ "near", // near far extension
+ "far", // near far extension
+ "__ptr32", // Microsoft
+ "__ptr64", // Microsoft
+ "__sptr", // Microsoft
+ "__uptr", // Microsoft
+ "dllimport", // Microsoft
+ "dllexport", // Microsoft
+ "thread", // Microsoft
+ "naked", // Microsoft
+ "microsoft_inline", // Microsoft
+ "forceinline", // Microsoft
+ "selectany", // Microsoft
+ "nothrow", // Microsoft
+ "novtable", // Microsoft
+ "noreturn", // Microsoft
+ "noinline", // Microsoft
+ "noalias", // Microsoft
+ "restrict", // Microsoft
+*/
+
+specifiers(
+ unique int id: @specifier,
+ unique string str: string ref
+);
+
+typespecifiers(
+ int type_id: @type ref,
+ int spec_id: @specifier ref
+);
+
+funspecifiers(
+ int func_id: @function ref,
+ int spec_id: @specifier ref
+);
+
+varspecifiers(
+ int var_id: @accessible ref,
+ int spec_id: @specifier ref
+);
+
+attributes(
+ unique int id: @attribute,
+ int kind: int ref,
+ string name: string ref,
+ string name_space: string ref,
+ int location: @location_default ref
+);
+
+case @attribute.kind of
+ 0 = @gnuattribute
+| 1 = @stdattribute
+| 2 = @declspec
+| 3 = @msattribute
+| 4 = @alignas
+// ... 5 @objc_propertyattribute deprecated
+;
+
+attribute_args(
+ unique int id: @attribute_arg,
+ int kind: int ref,
+ int attribute: @attribute ref,
+ int index: int ref,
+ int location: @location_default ref
+);
+
+case @attribute_arg.kind of
+ 0 = @attribute_arg_empty
+| 1 = @attribute_arg_token
+| 2 = @attribute_arg_constant
+| 3 = @attribute_arg_type
+| 4 = @attribute_arg_constant_expr
+;
+
+attribute_arg_value(
+ unique int arg: @attribute_arg ref,
+ string value: string ref
+);
+attribute_arg_type(
+ unique int arg: @attribute_arg ref,
+ int type_id: @type ref
+);
+attribute_arg_constant(
+ unique int arg: @attribute_arg ref,
+ int constant: @expr ref
+)
+attribute_arg_name(
+ unique int arg: @attribute_arg ref,
+ string name: string ref
+);
+
+typeattributes(
+ int type_id: @type ref,
+ int spec_id: @attribute ref
+);
+
+funcattributes(
+ int func_id: @function ref,
+ int spec_id: @attribute ref
+);
+
+varattributes(
+ int var_id: @accessible ref,
+ int spec_id: @attribute ref
+);
+
+stmtattributes(
+ int stmt_id: @stmt ref,
+ int spec_id: @attribute ref
+);
+
+@type = @builtintype
+ | @derivedtype
+ | @usertype
+ /* TODO | @fixedpointtype */
+ | @routinetype
+ | @ptrtomember
+ | @decltype;
+
+unspecifiedtype(
+ unique int type_id: @type ref,
+ int unspecified_type_id: @type ref
+);
+
+member(
+ int parent: @type ref,
+ int index: int ref,
+ int child: @member ref
+);
+
+@enclosingfunction_child = @usertype | @variable | @namespace
+
+enclosingfunction(
+ unique int child: @enclosingfunction_child ref,
+ int parent: @function ref
+);
+
+derivations(
+ unique int derivation: @derivation,
+ int sub: @type ref,
+ int index: int ref,
+ int super: @type ref,
+ int location: @location_default ref
+);
+
+derspecifiers(
+ int der_id: @derivation ref,
+ int spec_id: @specifier ref
+);
+
+/**
+ * Contains the byte offset of the base class subobject within the derived
+ * class. Only holds for non-virtual base classes, but see table
+ * `virtual_base_offsets` for offsets of virtual base class subobjects.
+ */
+direct_base_offsets(
+ unique int der_id: @derivation ref,
+ int offset: int ref
+);
+
+/**
+ * Contains the byte offset of the virtual base class subobject for class
+ * `super` within a most-derived object of class `sub`. `super` can be either a
+ * direct or indirect base class.
+ */
+#keyset[sub, super]
+virtual_base_offsets(
+ int sub: @usertype ref,
+ int super: @usertype ref,
+ int offset: int ref
+);
+
+frienddecls(
+ unique int id: @frienddecl,
+ int type_id: @type ref,
+ int decl_id: @declaration ref,
+ int location: @location_default ref
+);
+
+@declaredtype = @usertype ;
+
+@declaration = @function
+ | @declaredtype
+ | @variable
+ | @enumconstant
+ | @frienddecl;
+
+@member = @membervariable
+ | @function
+ | @declaredtype
+ | @enumconstant;
+
+@locatable = @diagnostic
+ | @declaration
+ | @ppd_include
+ | @ppd_define
+ | @macroinvocation
+ /*| @funcall*/
+ | @xmllocatable
+ | @attribute
+ | @attribute_arg;
+
+@namedscope = @namespace | @usertype;
+
+@element = @locatable
+ | @file
+ | @folder
+ | @specifier
+ | @type
+ | @expr
+ | @namespace
+ | @initialiser
+ | @stmt
+ | @derivation
+ | @comment
+ | @preprocdirect
+ | @fun_decl
+ | @var_decl
+ | @type_decl
+ | @namespace_decl
+ | @using
+ | @namequalifier
+ | @specialnamequalifyingelement
+ | @static_assert
+ | @type_mention
+ | @lambdacapture;
+
+@exprparent = @element;
+
+comments(
+ unique int id: @comment,
+ string contents: string ref,
+ int location: @location_default ref
+);
+
+commentbinding(
+ int id: @comment ref,
+ int element: @element ref
+);
+
+exprconv(
+ int converted: @expr ref,
+ unique int conversion: @expr ref
+);
+
+compgenerated(unique int id: @element ref);
+
+/**
+ * `destructor_call` destructs the `i`'th entity that should be
+ * destructed following `element`. Note that entities should be
+ * destructed in reverse construction order, so for a given `element`
+ * these should be called from highest to lowest `i`.
+ */
+#keyset[element, destructor_call]
+#keyset[element, i]
+synthetic_destructor_call(
+ int element: @element ref,
+ int i: int ref,
+ int destructor_call: @routineexpr ref
+);
+
+namespaces(
+ unique int id: @namespace,
+ string name: string ref
+);
+
+namespace_inline(
+ unique int id: @namespace ref
+);
+
+namespacembrs(
+ int parentid: @namespace ref,
+ unique int memberid: @namespacembr ref
+);
+
+@namespacembr = @declaration | @namespace;
+
+exprparents(
+ int expr_id: @expr ref,
+ int child_index: int ref,
+ int parent_id: @exprparent ref
+);
+
+expr_isload(unique int expr_id: @expr ref);
+
+@cast = @c_style_cast
+ | @const_cast
+ | @dynamic_cast
+ | @reinterpret_cast
+ | @static_cast
+ ;
+
+/*
+case @conversion.kind of
+ 0 = @simple_conversion // a numeric conversion, qualification conversion, or a reinterpret_cast
+| 1 = @bool_conversion // conversion to 'bool'
+| 2 = @base_class_conversion // a derived-to-base conversion
+| 3 = @derived_class_conversion // a base-to-derived conversion
+| 4 = @pm_base_class_conversion // a derived-to-base conversion of a pointer to member
+| 5 = @pm_derived_class_conversion // a base-to-derived conversion of a pointer to member
+| 6 = @glvalue_adjust // an adjustment of the type of a glvalue
+| 7 = @prvalue_adjust // an adjustment of the type of a prvalue
+;
+*/
+/**
+ * Describes the semantics represented by a cast expression. This is largely
+ * independent of the source syntax of the cast, so it is separate from the
+ * regular expression kind.
+ */
+conversionkinds(
+ unique int expr_id: @cast ref,
+ int kind: int ref
+);
+
+@conversion = @cast
+ | @array_to_pointer
+ | @parexpr
+ | @reference_to
+ | @ref_indirect
+ | @temp_init
+ ;
+
+/*
+case @funbindexpr.kind of
+ 0 = @normal_call // a normal call
+| 1 = @virtual_call // a virtual call
+| 2 = @adl_call // a call whose target is only found by ADL
+;
+*/
+iscall(unique int caller: @funbindexpr ref, int kind: int ref);
+
+numtemplatearguments(
+ unique int expr_id: @expr ref,
+ int num: int ref
+);
+
+specialnamequalifyingelements(
+ unique int id: @specialnamequalifyingelement,
+ unique string name: string ref
+);
+
+@namequalifiableelement = @expr | @namequalifier;
+@namequalifyingelement = @namespace
+ | @specialnamequalifyingelement
+ | @usertype;
+
+namequalifiers(
+ unique int id: @namequalifier,
+ unique int qualifiableelement: @namequalifiableelement ref,
+ int qualifyingelement: @namequalifyingelement ref,
+ int location: @location_default ref
+);
+
+varbind(
+ int expr: @varbindexpr ref,
+ int var: @accessible ref
+);
+
+funbind(
+ int expr: @funbindexpr ref,
+ int fun: @function ref
+);
+
+@any_new_expr = @new_expr
+ | @new_array_expr;
+
+@new_or_delete_expr = @any_new_expr
+ | @delete_expr
+ | @delete_array_expr;
+
+@prefix_crement_expr = @preincrexpr | @predecrexpr;
+
+@postfix_crement_expr = @postincrexpr | @postdecrexpr;
+
+@increment_expr = @preincrexpr | @postincrexpr;
+
+@decrement_expr = @predecrexpr | @postdecrexpr;
+
+@crement_expr = @increment_expr | @decrement_expr;
+
+@un_arith_op_expr = @arithnegexpr
+ | @unaryplusexpr
+ | @conjugation
+ | @realpartexpr
+ | @imagpartexpr
+ | @crement_expr
+ ;
+
+@un_bitwise_op_expr = @complementexpr;
+
+@un_log_op_expr = @notexpr;
+
+@un_op_expr = @address_of
+ | @indirect
+ | @un_arith_op_expr
+ | @un_bitwise_op_expr
+ | @builtinaddressof
+ | @vec_fill
+ | @un_log_op_expr
+ | @co_await
+ | @co_yield
+ ;
+
+@bin_log_op_expr = @andlogicalexpr | @orlogicalexpr;
+
+@cmp_op_expr = @eq_op_expr | @rel_op_expr;
+
+@eq_op_expr = @eqexpr | @neexpr;
+
+@rel_op_expr = @gtexpr
+ | @ltexpr
+ | @geexpr
+ | @leexpr
+ | @spaceshipexpr
+ ;
+
+@bin_bitwise_op_expr = @lshiftexpr
+ | @rshiftexpr
+ | @andexpr
+ | @orexpr
+ | @xorexpr
+ ;
+
+@p_arith_op_expr = @paddexpr
+ | @psubexpr
+ | @pdiffexpr
+ ;
+
+@bin_arith_op_expr = @addexpr
+ | @subexpr
+ | @mulexpr
+ | @divexpr
+ | @remexpr
+ | @jmulexpr
+ | @jdivexpr
+ | @fjaddexpr
+ | @jfaddexpr
+ | @fjsubexpr
+ | @jfsubexpr
+ | @minexpr
+ | @maxexpr
+ | @p_arith_op_expr
+ ;
+
+@bin_op_expr = @bin_arith_op_expr
+ | @bin_bitwise_op_expr
+ | @cmp_op_expr
+ | @bin_log_op_expr
+ ;
+
+@op_expr = @un_op_expr
+ | @bin_op_expr
+ | @assign_expr
+ | @conditionalexpr
+ ;
+
+@assign_arith_expr = @assignaddexpr
+ | @assignsubexpr
+ | @assignmulexpr
+ | @assigndivexpr
+ | @assignremexpr
+ ;
+
+@assign_bitwise_expr = @assignandexpr
+ | @assignorexpr
+ | @assignxorexpr
+ | @assignlshiftexpr
+ | @assignrshiftexpr
+ | @assignpaddexpr
+ | @assignpsubexpr
+ ;
+
+@assign_op_expr = @assign_arith_expr | @assign_bitwise_expr
+
+@assign_expr = @assignexpr | @assign_op_expr
+
+/*
+ case @allocator.form of
+ 0 = plain
+ | 1 = alignment
+ ;
+*/
+
+/**
+ * The allocator function associated with a `new` or `new[]` expression.
+ * The `form` column specified whether the allocation call contains an alignment
+ * argument.
+ */
+expr_allocator(
+ unique int expr: @any_new_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/*
+ case @deallocator.form of
+ 0 = plain
+ | 1 = size
+ | 2 = alignment
+ | 3 = size_and_alignment
+ ;
+*/
+
+/**
+ * The deallocator function associated with a `delete`, `delete[]`, `new`, or
+ * `new[]` expression. For a `new` or `new[]` expression, the deallocator is the
+ * one used to free memory if the initialization throws an exception.
+ * The `form` column specifies whether the deallocation call contains a size
+ * argument, and alignment argument, or both.
+ */
+expr_deallocator(
+ unique int expr: @new_or_delete_expr ref,
+ int func: @function ref,
+ int form: int ref
+);
+
+/**
+ * Holds if the `@conditionalexpr` is of the two operand form
+ * `guard ? : false`.
+ */
+expr_cond_two_operand(
+ unique int cond: @conditionalexpr ref
+);
+
+/**
+ * The guard of `@conditionalexpr` `guard ? true : false`
+ */
+expr_cond_guard(
+ unique int cond: @conditionalexpr ref,
+ int guard: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` holds. For the two operand form
+ * `guard ?: false` consider using `expr_cond_guard` instead.
+ */
+expr_cond_true(
+ unique int cond: @conditionalexpr ref,
+ int true: @expr ref
+);
+
+/**
+ * The expression used when the guard of `@conditionalexpr`
+ * `guard ? true : false` does not hold.
+ */
+expr_cond_false(
+ unique int cond: @conditionalexpr ref,
+ int false: @expr ref
+);
+
+/** A string representation of the value. */
+values(
+ unique int id: @value,
+ string str: string ref
+);
+
+/** The actual text in the source code for the value, if any. */
+valuetext(
+ unique int id: @value ref,
+ string text: string ref
+);
+
+valuebind(
+ int val: @value ref,
+ unique int expr: @expr ref
+);
+
+fieldoffsets(
+ unique int id: @variable ref,
+ int byteoffset: int ref,
+ int bitoffset: int ref
+);
+
+bitfield(
+ unique int id: @variable ref,
+ int bits: int ref,
+ int declared_bits: int ref
+);
+
+/* TODO
+memberprefix(
+ int member: @expr ref,
+ int prefix: @expr ref
+);
+*/
+
+/*
+ kind(1) = mbrcallexpr
+ kind(2) = mbrptrcallexpr
+ kind(3) = mbrptrmbrcallexpr
+ kind(4) = ptrmbrptrmbrcallexpr
+ kind(5) = mbrreadexpr // x.y
+ kind(6) = mbrptrreadexpr // p->y
+ kind(7) = mbrptrmbrreadexpr // x.*pm
+ kind(8) = mbrptrmbrptrreadexpr // x->*pm
+ kind(9) = staticmbrreadexpr // static x.y
+ kind(10) = staticmbrptrreadexpr // static p->y
+*/
+/* TODO
+memberaccess(
+ int member: @expr ref,
+ int kind: int ref
+);
+*/
+
+initialisers(
+ unique int init: @initialiser,
+ int var: @accessible ref,
+ unique int expr: @expr ref,
+ int location: @location_expr ref
+);
+
+braced_initialisers(
+ int init: @initialiser ref
+);
+
+/**
+ * An ancestor for the expression, for cases in which we cannot
+ * otherwise find the expression's parent.
+ */
+expr_ancestor(
+ int exp: @expr ref,
+ int ancestor: @element ref
+);
+
+exprs(
+ unique int id: @expr,
+ int kind: int ref,
+ int location: @location_expr ref
+);
+
+/*
+ case @value.category of
+ 1 = prval
+ | 2 = xval
+ | 3 = lval
+ ;
+*/
+expr_types(
+ int id: @expr ref,
+ int typeid: @type ref,
+ int value_category: int ref
+);
+
+case @expr.kind of
+ 1 = @errorexpr
+| 2 = @address_of // & AddressOfExpr
+| 3 = @reference_to // ReferenceToExpr (implicit?)
+| 4 = @indirect // * PointerDereferenceExpr
+| 5 = @ref_indirect // ReferenceDereferenceExpr (implicit?)
+// ...
+| 8 = @array_to_pointer // (???)
+| 9 = @vacuous_destructor_call // VacuousDestructorCall
+// ...
+| 11 = @assume // Microsoft
+| 12 = @parexpr
+| 13 = @arithnegexpr
+| 14 = @unaryplusexpr
+| 15 = @complementexpr
+| 16 = @notexpr
+| 17 = @conjugation // GNU ~ operator
+| 18 = @realpartexpr // GNU __real
+| 19 = @imagpartexpr // GNU __imag
+| 20 = @postincrexpr
+| 21 = @postdecrexpr
+| 22 = @preincrexpr
+| 23 = @predecrexpr
+| 24 = @conditionalexpr
+| 25 = @addexpr
+| 26 = @subexpr
+| 27 = @mulexpr
+| 28 = @divexpr
+| 29 = @remexpr
+| 30 = @jmulexpr // C99 mul imaginary
+| 31 = @jdivexpr // C99 div imaginary
+| 32 = @fjaddexpr // C99 add real + imaginary
+| 33 = @jfaddexpr // C99 add imaginary + real
+| 34 = @fjsubexpr // C99 sub real - imaginary
+| 35 = @jfsubexpr // C99 sub imaginary - real
+| 36 = @paddexpr // pointer add (pointer + int or int + pointer)
+| 37 = @psubexpr // pointer sub (pointer - integer)
+| 38 = @pdiffexpr // difference between two pointers
+| 39 = @lshiftexpr
+| 40 = @rshiftexpr
+| 41 = @andexpr
+| 42 = @orexpr
+| 43 = @xorexpr
+| 44 = @eqexpr
+| 45 = @neexpr
+| 46 = @gtexpr
+| 47 = @ltexpr
+| 48 = @geexpr
+| 49 = @leexpr
+| 50 = @minexpr // GNU minimum
+| 51 = @maxexpr // GNU maximum
+| 52 = @assignexpr
+| 53 = @assignaddexpr
+| 54 = @assignsubexpr
+| 55 = @assignmulexpr
+| 56 = @assigndivexpr
+| 57 = @assignremexpr
+| 58 = @assignlshiftexpr
+| 59 = @assignrshiftexpr
+| 60 = @assignandexpr
+| 61 = @assignorexpr
+| 62 = @assignxorexpr
+| 63 = @assignpaddexpr // assign pointer add
+| 64 = @assignpsubexpr // assign pointer sub
+| 65 = @andlogicalexpr
+| 66 = @orlogicalexpr
+| 67 = @commaexpr
+| 68 = @subscriptexpr // access to member of an array, e.g., a[5]
+// ... 69 @objc_subscriptexpr deprecated
+// ... 70 @cmdaccess deprecated
+// ...
+| 73 = @virtfunptrexpr
+| 74 = @callexpr
+// ... 75 @msgexpr_normal deprecated
+// ... 76 @msgexpr_super deprecated
+// ... 77 @atselectorexpr deprecated
+// ... 78 @atprotocolexpr deprecated
+| 79 = @vastartexpr
+| 80 = @vaargexpr
+| 81 = @vaendexpr
+| 82 = @vacopyexpr
+// ... 83 @atencodeexpr deprecated
+| 84 = @varaccess
+| 85 = @thisaccess
+// ... 86 @objc_box_expr deprecated
+| 87 = @new_expr
+| 88 = @delete_expr
+| 89 = @throw_expr
+| 90 = @condition_decl // a variable declared in a condition, e.g., if(int x = y > 2)
+| 91 = @braced_init_list
+| 92 = @type_id
+| 93 = @runtime_sizeof
+| 94 = @runtime_alignof
+| 95 = @sizeof_pack
+| 96 = @expr_stmt // GNU extension
+| 97 = @routineexpr
+| 98 = @type_operand // used to access a type in certain contexts (haven't found any examples yet....)
+| 99 = @offsetofexpr // offsetof ::= type and field
+| 100 = @hasassignexpr // __has_assign ::= type
+| 101 = @hascopyexpr // __has_copy ::= type
+| 102 = @hasnothrowassign // __has_nothrow_assign ::= type
+| 103 = @hasnothrowconstr // __has_nothrow_constructor ::= type
+| 104 = @hasnothrowcopy // __has_nothrow_copy ::= type
+| 105 = @hastrivialassign // __has_trivial_assign ::= type
+| 106 = @hastrivialconstr // __has_trivial_constructor ::= type
+| 107 = @hastrivialcopy // __has_trivial_copy ::= type
+| 108 = @hasuserdestr // __has_user_destructor ::= type
+| 109 = @hasvirtualdestr // __has_virtual_destructor ::= type
+| 110 = @isabstractexpr // __is_abstract ::= type
+| 111 = @isbaseofexpr // __is_base_of ::= type type
+| 112 = @isclassexpr // __is_class ::= type
+| 113 = @isconvtoexpr // __is_convertible_to ::= type type
+| 114 = @isemptyexpr // __is_empty ::= type
+| 115 = @isenumexpr // __is_enum ::= type
+| 116 = @ispodexpr // __is_pod ::= type
+| 117 = @ispolyexpr // __is_polymorphic ::= type
+| 118 = @isunionexpr // __is_union ::= type
+| 119 = @typescompexpr // GNU __builtin_types_compatible ::= type type
+| 120 = @intaddrexpr // EDG internal builtin, used to implement offsetof
+// ...
+| 122 = @hastrivialdestructor // __has_trivial_destructor ::= type
+| 123 = @literal
+| 124 = @uuidof
+| 127 = @aggregateliteral
+| 128 = @delete_array_expr
+| 129 = @new_array_expr
+// ... 130 @objc_array_literal deprecated
+// ... 131 @objc_dictionary_literal deprecated
+| 132 = @foldexpr
+// ...
+| 200 = @ctordirectinit
+| 201 = @ctorvirtualinit
+| 202 = @ctorfieldinit
+| 203 = @ctordelegatinginit
+| 204 = @dtordirectdestruct
+| 205 = @dtorvirtualdestruct
+| 206 = @dtorfielddestruct
+// ...
+| 210 = @static_cast
+| 211 = @reinterpret_cast
+| 212 = @const_cast
+| 213 = @dynamic_cast
+| 214 = @c_style_cast
+| 215 = @lambdaexpr
+| 216 = @param_ref
+| 217 = @noopexpr
+// ...
+| 294 = @istriviallyconstructibleexpr
+| 295 = @isdestructibleexpr
+| 296 = @isnothrowdestructibleexpr
+| 297 = @istriviallydestructibleexpr
+| 298 = @istriviallyassignableexpr
+| 299 = @isnothrowassignableexpr
+| 300 = @istrivialexpr
+| 301 = @isstandardlayoutexpr
+| 302 = @istriviallycopyableexpr
+| 303 = @isliteraltypeexpr
+| 304 = @hastrivialmoveconstructorexpr
+| 305 = @hastrivialmoveassignexpr
+| 306 = @hasnothrowmoveassignexpr
+| 307 = @isconstructibleexpr
+| 308 = @isnothrowconstructibleexpr
+| 309 = @hasfinalizerexpr
+| 310 = @isdelegateexpr
+| 311 = @isinterfaceclassexpr
+| 312 = @isrefarrayexpr
+| 313 = @isrefclassexpr
+| 314 = @issealedexpr
+| 315 = @issimplevalueclassexpr
+| 316 = @isvalueclassexpr
+| 317 = @isfinalexpr
+| 319 = @noexceptexpr
+| 320 = @builtinshufflevector
+| 321 = @builtinchooseexpr
+| 322 = @builtinaddressof
+| 323 = @vec_fill
+| 324 = @builtinconvertvector
+| 325 = @builtincomplex
+| 326 = @spaceshipexpr
+| 327 = @co_await
+| 328 = @co_yield
+| 329 = @temp_init
+| 330 = @isassignable
+| 331 = @isaggregate
+| 332 = @hasuniqueobjectrepresentations
+| 333 = @builtinbitcast
+| 334 = @builtinshuffle
+;
+
+@var_args_expr = @vastartexpr
+ | @vaendexpr
+ | @vaargexpr
+ | @vacopyexpr
+ ;
+
+@builtin_op = @var_args_expr
+ | @noopexpr
+ | @offsetofexpr
+ | @intaddrexpr
+ | @hasassignexpr
+ | @hascopyexpr
+ | @hasnothrowassign
+ | @hasnothrowconstr
+ | @hasnothrowcopy
+ | @hastrivialassign
+ | @hastrivialconstr
+ | @hastrivialcopy
+ | @hastrivialdestructor
+ | @hasuserdestr
+ | @hasvirtualdestr
+ | @isabstractexpr
+ | @isbaseofexpr
+ | @isclassexpr
+ | @isconvtoexpr
+ | @isemptyexpr
+ | @isenumexpr
+ | @ispodexpr
+ | @ispolyexpr
+ | @isunionexpr
+ | @typescompexpr
+ | @builtinshufflevector
+ | @builtinconvertvector
+ | @builtinaddressof
+ | @istriviallyconstructibleexpr
+ | @isdestructibleexpr
+ | @isnothrowdestructibleexpr
+ | @istriviallydestructibleexpr
+ | @istriviallyassignableexpr
+ | @isnothrowassignableexpr
+ | @isstandardlayoutexpr
+ | @istriviallycopyableexpr
+ | @isliteraltypeexpr
+ | @hastrivialmoveconstructorexpr
+ | @hastrivialmoveassignexpr
+ | @hasnothrowmoveassignexpr
+ | @isconstructibleexpr
+ | @isnothrowconstructibleexpr
+ | @hasfinalizerexpr
+ | @isdelegateexpr
+ | @isinterfaceclassexpr
+ | @isrefarrayexpr
+ | @isrefclassexpr
+ | @issealedexpr
+ | @issimplevalueclassexpr
+ | @isvalueclassexpr
+ | @isfinalexpr
+ | @builtinchooseexpr
+ | @builtincomplex
+ | @isassignable
+ | @isaggregate
+ | @hasuniqueobjectrepresentations
+ | @builtinbitcast
+ | @builtinshuffle
+ ;
+
+new_allocated_type(
+ unique int expr: @new_expr ref,
+ int type_id: @type ref
+);
+
+new_array_allocated_type(
+ unique int expr: @new_array_expr ref,
+ int type_id: @type ref
+);
+
+/**
+ * The field being initialized by an initializer expression within an aggregate
+ * initializer for a class/struct/union.
+ */
+#keyset[aggregate, field]
+aggregate_field_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int field: @membervariable ref
+);
+
+/**
+ * The index of the element being initialized by an initializer expression
+ * within an aggregate initializer for an array.
+ */
+#keyset[aggregate, element_index]
+aggregate_array_init(
+ int aggregate: @aggregateliteral ref,
+ int initializer: @expr ref,
+ int element_index: int ref
+);
+
+@ctorinit = @ctordirectinit
+ | @ctorvirtualinit
+ | @ctorfieldinit
+ | @ctordelegatinginit;
+@dtordestruct = @dtordirectdestruct
+ | @dtorvirtualdestruct
+ | @dtorfielddestruct;
+
+
+condition_decl_bind(
+ unique int expr: @condition_decl ref,
+ unique int decl: @declaration ref
+);
+
+typeid_bind(
+ unique int expr: @type_id ref,
+ int type_id: @type ref
+);
+
+uuidof_bind(
+ unique int expr: @uuidof ref,
+ int type_id: @type ref
+);
+
+@runtime_sizeof_or_alignof = @runtime_sizeof | @runtime_alignof;
+
+sizeof_bind(
+ unique int expr: @runtime_sizeof_or_alignof ref,
+ int type_id: @type ref
+);
+
+code_block(
+ unique int block: @literal ref,
+ unique int routine: @function ref
+);
+
+lambdas(
+ unique int expr: @lambdaexpr ref,
+ string default_capture: string ref,
+ boolean has_explicit_return_type: boolean ref
+);
+
+lambda_capture(
+ unique int id: @lambdacapture,
+ int lambda: @lambdaexpr ref,
+ int index: int ref,
+ int field: @membervariable ref,
+ boolean captured_by_reference: boolean ref,
+ boolean is_implicit: boolean ref,
+ int location: @location_default ref
+);
+
+@funbindexpr = @routineexpr
+ | @new_expr
+ | @delete_expr
+ | @delete_array_expr
+ | @ctordirectinit
+ | @ctorvirtualinit
+ | @ctordelegatinginit
+ | @dtordirectdestruct
+ | @dtorvirtualdestruct;
+
+@varbindexpr = @varaccess | @ctorfieldinit | @dtorfielddestruct;
+@addressable = @function | @variable ;
+@accessible = @addressable | @enumconstant ;
+
+@access = @varaccess | @routineexpr ;
+
+fold(
+ int expr: @foldexpr ref,
+ string operator: string ref,
+ boolean is_left_fold: boolean ref
+);
+
+stmts(
+ unique int id: @stmt,
+ int kind: int ref,
+ int location: @location_stmt ref
+);
+
+case @stmt.kind of
+ 1 = @stmt_expr
+| 2 = @stmt_if
+| 3 = @stmt_while
+| 4 = @stmt_goto
+| 5 = @stmt_label
+| 6 = @stmt_return
+| 7 = @stmt_block
+| 8 = @stmt_end_test_while // do { ... } while ( ... )
+| 9 = @stmt_for
+| 10 = @stmt_switch_case
+| 11 = @stmt_switch
+| 13 = @stmt_asm // "asm" statement or the body of an asm function
+| 15 = @stmt_try_block
+| 16 = @stmt_microsoft_try // Microsoft
+| 17 = @stmt_decl
+| 18 = @stmt_set_vla_size // C99
+| 19 = @stmt_vla_decl // C99
+| 25 = @stmt_assigned_goto // GNU
+| 26 = @stmt_empty
+| 27 = @stmt_continue
+| 28 = @stmt_break
+| 29 = @stmt_range_based_for // C++11
+// ... 30 @stmt_at_autoreleasepool_block deprecated
+// ... 31 @stmt_objc_for_in deprecated
+// ... 32 @stmt_at_synchronized deprecated
+| 33 = @stmt_handler
+// ... 34 @stmt_finally_end deprecated
+| 35 = @stmt_constexpr_if
+| 37 = @stmt_co_return
+;
+
+type_vla(
+ int type_id: @type ref,
+ int decl: @stmt_vla_decl ref
+);
+
+variable_vla(
+ int var: @variable ref,
+ int decl: @stmt_vla_decl ref
+);
+
+if_initialization(
+ unique int if_stmt: @stmt_if ref,
+ int init_id: @stmt ref
+);
+
+if_then(
+ unique int if_stmt: @stmt_if ref,
+ int then_id: @stmt ref
+);
+
+if_else(
+ unique int if_stmt: @stmt_if ref,
+ int else_id: @stmt ref
+);
+
+constexpr_if_initialization(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int init_id: @stmt ref
+);
+
+constexpr_if_then(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int then_id: @stmt ref
+);
+
+constexpr_if_else(
+ unique int constexpr_if_stmt: @stmt_constexpr_if ref,
+ int else_id: @stmt ref
+);
+
+while_body(
+ unique int while_stmt: @stmt_while ref,
+ int body_id: @stmt ref
+);
+
+do_body(
+ unique int do_stmt: @stmt_end_test_while ref,
+ int body_id: @stmt ref
+);
+
+switch_initialization(
+ unique int switch_stmt: @stmt_switch ref,
+ int init_id: @stmt ref
+);
+
+#keyset[switch_stmt, index]
+switch_case(
+ int switch_stmt: @stmt_switch ref,
+ int index: int ref,
+ int case_id: @stmt_switch_case ref
+);
+
+switch_body(
+ unique int switch_stmt: @stmt_switch ref,
+ int body_id: @stmt ref
+);
+
+for_initialization(
+ unique int for_stmt: @stmt_for ref,
+ int init_id: @stmt ref
+);
+
+for_condition(
+ unique int for_stmt: @stmt_for ref,
+ int condition_id: @expr ref
+);
+
+for_update(
+ unique int for_stmt: @stmt_for ref,
+ int update_id: @expr ref
+);
+
+for_body(
+ unique int for_stmt: @stmt_for ref,
+ int body_id: @stmt ref
+);
+
+@stmtparent = @stmt | @expr_stmt ;
+stmtparents(
+ unique int id: @stmt ref,
+ int index: int ref,
+ int parent: @stmtparent ref
+);
+
+ishandler(unique int block: @stmt_block ref);
+
+@cfgnode = @stmt | @expr | @function | @initialiser ;
+
+stmt_decl_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl: @declaration ref
+);
+
+stmt_decl_entry_bind(
+ int stmt: @stmt_decl ref,
+ int num: int ref,
+ int decl_entry: @element ref
+);
+
+@functionorblock = @function | @stmt_block;
+
+blockscope(
+ unique int block: @stmt_block ref,
+ int enclosing: @functionorblock ref
+);
+
+@jump = @stmt_goto | @stmt_break | @stmt_continue;
+
+@jumporlabel = @jump | @stmt_label | @literal;
+
+jumpinfo(
+ unique int id: @jumporlabel ref,
+ string str: string ref,
+ int target: @stmt ref
+);
+
+preprocdirects(
+ unique int id: @preprocdirect,
+ int kind: int ref,
+ int location: @location_default ref
+);
+case @preprocdirect.kind of
+ 0 = @ppd_if
+| 1 = @ppd_ifdef
+| 2 = @ppd_ifndef
+| 3 = @ppd_elif
+| 4 = @ppd_else
+| 5 = @ppd_endif
+| 6 = @ppd_plain_include
+| 7 = @ppd_define
+| 8 = @ppd_undef
+| 9 = @ppd_line
+| 10 = @ppd_error
+| 11 = @ppd_pragma
+| 12 = @ppd_objc_import
+| 13 = @ppd_include_next
+| 18 = @ppd_warning
+;
+
+@ppd_include = @ppd_plain_include | @ppd_objc_import | @ppd_include_next;
+
+@ppd_branch = @ppd_if | @ppd_ifdef | @ppd_ifndef | @ppd_elif;
+
+preprocpair(
+ int begin : @ppd_branch ref,
+ int elseelifend : @preprocdirect ref
+);
+
+preproctrue(int branch : @ppd_branch ref);
+preprocfalse(int branch : @ppd_branch ref);
+
+preproctext(
+ unique int id: @preprocdirect ref,
+ string head: string ref,
+ string body: string ref
+);
+
+includes(
+ unique int id: @ppd_include ref,
+ int included: @file ref
+);
+
+link_targets(
+ unique int id: @link_target,
+ int binary: @file ref
+);
+
+link_parent(
+ int element : @element ref,
+ int link_target : @link_target ref
+);
+
+/* XML Files */
+
+xmlEncoding(unique int id: @file ref, string encoding: string ref);
+
+xmlDTDs(
+ unique int id: @xmldtd,
+ string root: string ref,
+ string publicId: string ref,
+ string systemId: string ref,
+ int fileid: @file ref
+);
+
+xmlElements(
+ unique int id: @xmlelement,
+ string name: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlAttrs(
+ unique int id: @xmlattribute,
+ int elementid: @xmlelement ref,
+ string name: string ref,
+ string value: string ref,
+ int idx: int ref,
+ int fileid: @file ref
+);
+
+xmlNs(
+ int id: @xmlnamespace,
+ string prefixName: string ref,
+ string URI: string ref,
+ int fileid: @file ref
+);
+
+xmlHasNs(
+ int elementId: @xmlnamespaceable ref,
+ int nsId: @xmlnamespace ref,
+ int fileid: @file ref
+);
+
+xmlComments(
+ unique int id: @xmlcomment,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int fileid: @file ref
+);
+
+xmlChars(
+ unique int id: @xmlcharacters,
+ string text: string ref,
+ int parentid: @xmlparent ref,
+ int idx: int ref,
+ int isCDATA: int ref,
+ int fileid: @file ref
+);
+
+@xmlparent = @file | @xmlelement;
+@xmlnamespaceable = @xmlelement | @xmlattribute;
+
+xmllocations(
+ int xmlElement: @xmllocatable ref,
+ int location: @location_default ref
+);
+
+@xmllocatable = @xmlcharacters
+ | @xmlelement
+ | @xmlcomment
+ | @xmlattribute
+ | @xmldtd
+ | @file
+ | @xmlnamespace;
diff --git a/cpp/ql/lib/upgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/upgrade.properties b/cpp/ql/lib/upgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/upgrade.properties
new file mode 100644
index 00000000000..1b45a2696ee
--- /dev/null
+++ b/cpp/ql/lib/upgrades/23f7cbb88a4eb29f30c3490363dc201bc054c5ff/upgrade.properties
@@ -0,0 +1,2 @@
+description: Support all constant attribute arguments
+compatibility: partial
diff --git a/cpp/ql/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md
index 449af46b6b8..ae7e4f7151b 100644
--- a/cpp/ql/src/CHANGELOG.md
+++ b/cpp/ql/src/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.3.1
+
+## 0.3.0
+
+### Breaking Changes
+
+* Contextual queries and the query libraries they depend on have been moved to the `codeql/cpp-all` package.
+
+## 0.2.0
+
## 0.1.4
## 0.1.3
diff --git a/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql b/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql
index dee723e2686..3cbcffe0ce3 100644
--- a/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql
+++ b/cpp/ql/src/Likely Bugs/Conversion/LossyFunctionResultCast.ql
@@ -44,7 +44,7 @@ predicate whiteListWrapped(FunctionCall fc) {
from FunctionCall c, FloatingPointType t1, IntegralType t2
where
- t1 = c.getTarget().getType().getUnderlyingType() and
+ pragma[only_bind_into](t1) = c.getTarget().getType().getUnderlyingType() and
t2 = c.getActualType() and
c.hasImplicitConversion() and
not whiteListWrapped(c)
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql
index e60b763bc53..3d2f6830bf5 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/AssignWhereCompareMeant.ql
@@ -68,7 +68,7 @@ class BooleanControllingAssignmentInExpr extends BooleanControllingAssignment {
// if((a = b) && use_value(a)) { ... }
// ```
// where the assignment is meant to update the value of `a` before it's used in some other boolean
- // subexpression that is guarenteed to be evaluate _after_ the assignment.
+ // subexpression that is guaranteed to be evaluate _after_ the assignment.
this.isParenthesised() and
exists(LogicalAndExpr parent, Variable var, VariableAccess access |
var = this.getLValue().(VariableAccess).getTarget() and
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql b/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql
index 30664869adc..9c0230d7514 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/IncorrectNotOperatorUsage.ql
@@ -10,7 +10,6 @@
* @precision medium
* @tags security
* external/cwe/cwe-480
- * external/microsoft/c6317
*/
import cpp
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql
index 074c82bc03b..8770d249497 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/UsingStrcpyAsBoolean.ql
@@ -7,8 +7,7 @@
* @problem.severity error
* @precision high
* @id cpp/string-copy-return-value-as-boolean
- * @tags external/microsoft/C6324
- * correctness
+ * @tags correctness
*/
import cpp
diff --git a/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql b/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql
index 5e3af347821..38cda5d0560 100644
--- a/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql
+++ b/cpp/ql/src/Likely Bugs/Likely Typos/inconsistentLoopDirection.ql
@@ -7,7 +7,6 @@
* @id cpp/inconsistent-loop-direction
* @tags correctness
* external/cwe/cwe-835
- * external/microsoft/6293
* @msrc.severity important
*/
@@ -52,7 +51,7 @@ predicate illDefinedDecrForStmt(
(
upperBound(initialCondition) < lowerBound(terminalCondition) and
(
- // exclude cases where the loop counter is `unsigned` (where wrapping behaviour can be used deliberately)
+ // exclude cases where the loop counter is `unsigned` (where wrapping behavior can be used deliberately)
v.getUnspecifiedType().(IntegralType).isSigned() or
initialCondition.getValue().toInt() = 0
)
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
index c72e25f61df..ed1d4084993 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql
@@ -18,7 +18,7 @@ import semmle.code.cpp.ir.IR
import semmle.code.cpp.ir.dataflow.MustFlow
import PathGraph
-/** Holds if `f` has a name that we intrepret as evidence of intentionally returning the value of the stack pointer. */
+/** Holds if `f` has a name that we interpret as evidence of intentionally returning the value of the stack pointer. */
predicate intentionallyReturnsStackPointer(Function f) {
f.getName().toLowerCase().matches(["%stack%", "%sp%"])
}
@@ -52,6 +52,18 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
)
}
+ // We disable flow into callables in this query as we'd otherwise get a result on this piece of code:
+ // ```cpp
+ // int* id(int* px) {
+ // return px; // this returns the local variable `x`, but it's fine as the local variable isn't declared in this scope.
+ // }
+ // void f() {
+ // int x;
+ // int* px = id(&x);
+ // }
+ // ```
+ override predicate allowInterproceduralFlow() { none() }
+
/**
* This configuration intentionally conflates addresses of fields and their object, and pointer offsets
* with their base pointer as this allows us to detect cases where an object's address flows to a
@@ -74,13 +86,9 @@ class ReturnStackAllocatedMemoryConfig extends MustFlowConfiguration {
from
MustFlowPathNode source, MustFlowPathNode sink, VariableAddressInstruction var,
- ReturnStackAllocatedMemoryConfig conf, Function f
+ ReturnStackAllocatedMemoryConfig conf
where
- conf.hasFlowPath(source, sink) and
- source.getNode().asInstruction() = var and
- // Only raise an alert if we're returning from the _same_ callable as the on that
- // declared the stack variable.
- var.getEnclosingFunction() = pragma[only_bind_into](f) and
- sink.getNode().getEnclosingCallable() = pragma[only_bind_into](f)
+ 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()
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql
index f7eca2304b3..a3a2c5b83be 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/StrncpyFlippedArgs.ql
@@ -18,6 +18,7 @@
import cpp
import Buffer
private import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+private import semmle.code.cpp.models.implementations.Strcpy
predicate isSizePlus(Expr e, BufferSizeExpr baseSize, int plus) {
// baseSize
@@ -41,33 +42,6 @@ predicate isSizePlus(Expr e, BufferSizeExpr baseSize, int plus) {
)
}
-predicate strncpyFunction(Function f, int argDest, int argSrc, int argLimit) {
- exists(string name | name = f.getName() |
- name =
- [
- "strcpy_s", // strcpy_s(dst, max_amount, src)
- "wcscpy_s", // wcscpy_s(dst, max_amount, src)
- "_mbscpy_s" // _mbscpy_s(dst, max_amount, src)
- ] and
- argDest = 0 and
- argSrc = 2 and
- argLimit = 1
- or
- name =
- [
- "strncpy", // strncpy(dst, src, max_amount)
- "strncpy_l", // strncpy_l(dst, src, max_amount, locale)
- "wcsncpy", // wcsncpy(dst, src, max_amount)
- "_wcsncpy_l", // _wcsncpy_l(dst, src, max_amount, locale)
- "_mbsncpy", // _mbsncpy(dst, src, max_amount)
- "_mbsncpy_l" // _mbsncpy_l(dst, src, max_amount, locale)
- ] and
- argDest = 0 and
- argSrc = 1 and
- argLimit = 2
- )
-}
-
string nthString(int num) {
num = 0 and
result = "first"
@@ -96,11 +70,13 @@ int arrayExprFixedSize(Expr e) {
}
from
- Function f, FunctionCall fc, int argDest, int argSrc, int argLimit, int charSize, Access copyDest,
- Access copySource, string name, string nth
+ StrcpyFunction f, FunctionCall fc, int argDest, int argSrc, int argLimit, int charSize,
+ Access copyDest, Access copySource, string name, string nth
where
f = fc.getTarget() and
- strncpyFunction(f, argDest, argSrc, argLimit) and
+ argDest = f.getParamDest() and
+ argSrc = f.getParamSrc() and
+ argLimit = f.getParamSize() and
copyDest = fc.getArgument(argDest) and
copySource = fc.getArgument(argSrc) and
// Some of the functions operate on a larger char type, like `wchar_t`, so we
diff --git a/cpp/ql/src/Likely Bugs/Memory Management/UsingExpiredStackAddress.ql b/cpp/ql/src/Likely Bugs/Memory Management/UsingExpiredStackAddress.ql
index 27aeabbaf49..3f3997315d4 100644
--- a/cpp/ql/src/Likely Bugs/Memory Management/UsingExpiredStackAddress.ql
+++ b/cpp/ql/src/Likely Bugs/Memory Management/UsingExpiredStackAddress.ql
@@ -133,7 +133,9 @@ TGlobalAddress globalAddress(Instruction instr) {
)
or
exists(FieldAddressInstruction fai | instr = fai |
- result = TFieldAddress(globalAddress(fai.getObjectAddress()), fai.getField())
+ result =
+ TFieldAddress(globalAddress(pragma[only_bind_into](fai.getObjectAddress())),
+ pragma[only_bind_out](fai.getField()))
)
or
result = globalAddress(instr.(PointerOffsetInstruction).getLeft())
diff --git a/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql b/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql
index 6bb411b7844..054fdccbb99 100644
--- a/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql
+++ b/cpp/ql/src/Likely Bugs/UseInOwnInitializer.ql
@@ -15,6 +15,7 @@ class VariableAccessInInitializer extends VariableAccess {
Variable var;
Initializer init;
+ pragma[nomagic]
VariableAccessInInitializer() {
init.getDeclaration() = var and
init.getExpr().getAChild*() = this
diff --git a/cpp/ql/src/Metrics/Internal/IRConsistency.ql b/cpp/ql/src/Metrics/Internal/IRConsistency.ql
new file mode 100644
index 00000000000..5f45d15b8e2
--- /dev/null
+++ b/cpp/ql/src/Metrics/Internal/IRConsistency.ql
@@ -0,0 +1,37 @@
+/**
+ * @name Count IR inconsistencies
+ * @description Counts the various IR inconsistencies that may occur.
+ * This query is for internal use only and may change without notice.
+ * @kind table
+ * @id cpp/count-ir-inconsistencies
+ */
+
+import cpp
+import semmle.code.cpp.ir.implementation.aliased_ssa.IR
+import semmle.code.cpp.ir.implementation.aliased_ssa.IRConsistency as IRConsistency
+
+select count(Instruction i | IRConsistency::missingOperand(i, _, _, _) | i) as missingOperand,
+ count(Instruction i | IRConsistency::unexpectedOperand(i, _, _, _) | i) as unexpectedOperand,
+ count(Instruction i | IRConsistency::duplicateOperand(i, _, _, _) | i) as duplicateOperand,
+ count(PhiInstruction i | IRConsistency::missingPhiOperand(i, _, _, _) | i) as missingPhiOperand,
+ count(Operand o | IRConsistency::missingOperandType(o, _, _, _) | o) as missingOperandType,
+ count(ChiInstruction i | IRConsistency::duplicateChiOperand(i, _, _, _) | i) as duplicateChiOperand,
+ count(Instruction i | IRConsistency::sideEffectWithoutPrimary(i, _, _, _) | i) as sideEffectWithoutPrimary,
+ count(Instruction i | IRConsistency::instructionWithoutSuccessor(i, _, _, _) | i) as instructionWithoutSuccessor,
+ count(Instruction i | IRConsistency::ambiguousSuccessors(i, _, _, _) | i) as ambiguousSuccessors,
+ count(Instruction i | IRConsistency::unexplainedLoop(i, _, _, _) | i) as unexplainedLoop,
+ count(PhiInstruction i | IRConsistency::unnecessaryPhiInstruction(i, _, _, _) | i) as unnecessaryPhiInstruction,
+ count(Instruction i | IRConsistency::memoryOperandDefinitionIsUnmodeled(i, _, _, _) | i) as memoryOperandDefinitionIsUnmodeled,
+ count(Operand o | IRConsistency::operandAcrossFunctions(o, _, _, _, _, _) | o) as operandAcrossFunctions,
+ count(IRFunction f | IRConsistency::containsLoopOfForwardEdges(f, _) | f) as containsLoopOfForwardEdges,
+ count(IRBlock i | IRConsistency::lostReachability(i, _, _, _) | i) as lostReachability,
+ count(string m | IRConsistency::backEdgeCountMismatch(_, m) | m) as backEdgeCountMismatch,
+ count(Operand o | IRConsistency::useNotDominatedByDefinition(o, _, _, _) | o) as useNotDominatedByDefinition,
+ count(SwitchInstruction i | IRConsistency::switchInstructionWithoutDefaultEdge(i, _, _, _) | i) as switchInstructionWithoutDefaultEdge,
+ count(Instruction i | IRConsistency::notMarkedAsConflated(i, _, _, _) | i) as notMarkedAsConflated,
+ count(Instruction i | IRConsistency::wronglyMarkedAsConflated(i, _, _, _) | i) as wronglyMarkedAsConflated,
+ count(MemoryOperand o | IRConsistency::invalidOverlap(o, _, _, _) | o) as invalidOverlap,
+ count(Instruction i | IRConsistency::nonUniqueEnclosingIRFunction(i, _, _, _) | i) as nonUniqueEnclosingIRFunction,
+ count(FieldAddressInstruction i | IRConsistency::fieldAddressOnNonPointer(i, _, _, _) | i) as fieldAddressOnNonPointer,
+ count(Instruction i | IRConsistency::thisArgumentIsNonPointer(i, _, _, _) | i) as thisArgumentIsNonPointer,
+ count(Instruction i | IRConsistency::nonUniqueIRVariable(i, _, _, _) | i) as nonUniqueIRVariable
diff --git a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql
index 73fcf034096..7a3877f638c 100644
--- a/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql
+++ b/cpp/ql/src/Security/CWE/CWE-078/ExecTainted.ql
@@ -77,7 +77,7 @@ class ExecState extends DataFlow::FlowState {
ExecState() {
this =
"ExecState (" + fst.getLocation() + " | " + fst + ", " + snd.getLocation() + " | " + snd + ")" and
- interestingConcatenation(fst, snd)
+ interestingConcatenation(pragma[only_bind_into](fst), pragma[only_bind_into](snd))
}
DataFlow::Node getFstNode() { result = fst }
diff --git a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql
index 67ba5b0c45b..eb746e2d1d2 100644
--- a/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql
+++ b/cpp/ql/src/Security/CWE/CWE-253/HResultBooleanConversion.ql
@@ -8,11 +8,6 @@
* @precision high
* @tags security
* external/cwe/cwe-253
- * external/microsoft/C6214
- * external/microsoft/C6215
- * external/microsoft/C6216
- * external/microsoft/C6217
- * external/microsoft/C6230
*/
import cpp
diff --git a/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql b/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
index 7c540e9d313..ff8e85cecec 100644
--- a/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
+++ b/cpp/ql/src/Security/CWE/CWE-428/UnsafeCreateProcessCall.ql
@@ -9,7 +9,6 @@
* @msrc.severity important
* @tags security
* external/cwe/cwe-428
- * external/microsoft/C6277
*/
import cpp
diff --git a/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql b/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql
index 65551a1f138..aee4f3c8405 100644
--- a/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql
+++ b/cpp/ql/src/Security/CWE/CWE-704/WcharCharConversion.ql
@@ -10,7 +10,6 @@
* @precision high
* @tags security
* external/cwe/cwe-704
- * external/microsoft/c/c6276
*/
import cpp
diff --git a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql
index 81998bda450..482b5daf992 100644
--- a/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql
+++ b/cpp/ql/src/Security/CWE/CWE-732/UnsafeDaclSecurityDescriptor.ql
@@ -11,7 +11,6 @@
* @precision high
* @tags security
* external/cwe/cwe-732
- * external/microsoft/C6248
*/
import cpp
diff --git a/cpp/ql/src/change-notes/2021-08-10-use-strcpyfunction-in-bad-strncpy-size.md b/cpp/ql/src/change-notes/2021-08-10-use-strcpyfunction-in-bad-strncpy-size.md
new file mode 100644
index 00000000000..3468fec4c8d
--- /dev/null
+++ b/cpp/ql/src/change-notes/2021-08-10-use-strcpyfunction-in-bad-strncpy-size.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* The query `cpp/bad-strncpy-size` now covers more `strncpy`-like functions than before, including `strxfrm`(`_l`), `wcsxfrm`(`_l`), and `stpncpy`. Users of this query may see an increase in results.
diff --git a/cpp/ql/src/change-notes/released/0.2.0.md b/cpp/ql/src/change-notes/released/0.2.0.md
new file mode 100644
index 00000000000..79a5f33514f
--- /dev/null
+++ b/cpp/ql/src/change-notes/released/0.2.0.md
@@ -0,0 +1 @@
+## 0.2.0
diff --git a/cpp/ql/src/change-notes/released/0.3.0.md b/cpp/ql/src/change-notes/released/0.3.0.md
new file mode 100644
index 00000000000..75d99f333c9
--- /dev/null
+++ b/cpp/ql/src/change-notes/released/0.3.0.md
@@ -0,0 +1,5 @@
+## 0.3.0
+
+### Breaking Changes
+
+* Contextual queries and the query libraries they depend on have been moved to the `codeql/cpp-all` package.
diff --git a/cpp/ql/src/change-notes/released/0.3.1.md b/cpp/ql/src/change-notes/released/0.3.1.md
new file mode 100644
index 00000000000..2b0719929a1
--- /dev/null
+++ b/cpp/ql/src/change-notes/released/0.3.1.md
@@ -0,0 +1 @@
+## 0.3.1
diff --git a/cpp/ql/src/codeql-pack.release.yml b/cpp/ql/src/codeql-pack.release.yml
index e8ee3af8ef9..bb106b1cb63 100644
--- a/cpp/ql/src/codeql-pack.release.yml
+++ b/cpp/ql/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.1.4
+lastReleaseVersion: 0.3.1
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.cpp
new file mode 100644
index 00000000000..3b71c4f3005
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.cpp
@@ -0,0 +1,6 @@
+
+...
+mbtowc(&wc, ptr, 4)); // BAD:we can get unpredictable results
+...
+mbtowc(&wc, ptr, MB_LEN_MAX); // GOOD
+...
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp
new file mode 100644
index 00000000000..e6f18c5c8ae
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp
@@ -0,0 +1,23 @@
+
+
+
+ Using a function to convert multibyte or wide characters with an invalid length argument may result in an out-of-range access error or unexpected results.
+
+
+
+
+The following example shows the erroneous and corrected method of using function mbtowc.
+
+
+
+
+
+
+ CERT Coding Standard:
+ ARR30-C. Do not form or use out-of-bounds pointers or array subscripts - SEI CERT C Coding Standard - Confluence.
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql
new file mode 100644
index 00000000000..f73ba21c39b
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql
@@ -0,0 +1,236 @@
+/**
+ * @name Dangerous use convert function.
+ * @description Using convert function with an invalid length argument can result in an out-of-bounds access error or unexpected result.
+ * @kind problem
+ * @id cpp/dangerous-use-convert-function
+ * @problem.severity warning
+ * @precision medium
+ * @tags correctness
+ * security
+ * external/cwe/cwe-125
+ */
+
+import cpp
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+
+/** Holds if there are indications that the variable is treated as a string. */
+predicate exprMayBeString(Expr exp) {
+ (
+ exists(StringLiteral sl | globalValueNumber(exp) = globalValueNumber(sl))
+ or
+ exists(FunctionCall fctmp |
+ (
+ fctmp.getAnArgument().(VariableAccess).getTarget() = exp.(VariableAccess).getTarget() or
+ globalValueNumber(fctmp.getAnArgument()) = globalValueNumber(exp)
+ ) and
+ fctmp.getTarget().hasName(["strlen", "strcat", "strncat", "strcpy", "sptintf", "printf"])
+ )
+ or
+ exists(AssignExpr astmp |
+ astmp.getRValue().getValue() = "0" and
+ astmp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() =
+ exp.(VariableAccess).getTarget()
+ )
+ or
+ exists(ComparisonOperation cotmp, Expr exptmp1, Expr exptmp2 |
+ exptmp1.getValue() = "0" and
+ (
+ exptmp2.(PointerDereferenceExpr).getOperand().(VariableAccess).getTarget() =
+ exp.(VariableAccess).getTarget() or
+ exptmp2.(ArrayExpr).getArrayBase().(VariableAccess).getTarget() =
+ exp.getAChild().(VariableAccess).getTarget()
+ ) and
+ cotmp.hasOperands(exptmp1, exptmp2)
+ )
+ )
+}
+
+/** Holds if expression is constant or operator call `sizeof`. */
+predicate argConstOrSizeof(Expr exp) {
+ exp.getValue().toInt() > 1 or
+ exp.(SizeofTypeOperator).getTypeOperand().getSize() > 1
+}
+
+/** Holds if expression is macro. */
+predicate argMacro(Expr exp) {
+ exists(MacroInvocation matmp |
+ exp = matmp.getExpr() and
+ (
+ matmp.getMacroName() = "MB_LEN_MAX" or
+ matmp.getMacroName() = "MB_CUR_MAX"
+ )
+ )
+}
+
+/** Holds if erroneous situations of using functions `mbtowc` and `mbrtowc` are detected. */
+predicate findUseCharacterConversion(Expr exp, string msg) {
+ exists(FunctionCall fc |
+ fc = exp and
+ (
+ exists(Loop lptmp | lptmp = fc.getEnclosingStmt().getParentStmt*()) and
+ fc.getTarget().hasName(["mbtowc", "mbrtowc", "_mbtowc_l"]) and
+ not fc.getArgument(0).isConstant() and
+ not fc.getArgument(1).isConstant() and
+ (
+ exprMayBeString(fc.getArgument(1)) and
+ argConstOrSizeof(fc.getArgument(2)) and
+ fc.getArgument(2).getValue().toInt() < 5 and
+ not argMacro(fc.getArgument(2)) and
+ msg = "Size can be less than maximum character length, use macro MB_CUR_MAX."
+ or
+ not exprMayBeString(fc.getArgument(1)) and
+ (
+ argConstOrSizeof(fc.getArgument(2))
+ or
+ argMacro(fc.getArgument(2))
+ or
+ exists(DecrementOperation dotmp |
+ globalValueNumber(dotmp.getAnOperand()) = globalValueNumber(fc.getArgument(2)) and
+ not exists(AssignSubExpr aetmp |
+ (
+ aetmp.getLValue().(VariableAccess).getTarget() =
+ fc.getArgument(2).(VariableAccess).getTarget() or
+ globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(2))
+ ) and
+ globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc)
+ )
+ )
+ ) and
+ msg =
+ "Access beyond the allocated memory is possible, the length can change without changing the pointer."
+ or
+ exists(AssignPointerAddExpr aetmp |
+ (
+ aetmp.getLValue().(VariableAccess).getTarget() =
+ fc.getArgument(0).(VariableAccess).getTarget() or
+ globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(0))
+ ) and
+ globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc)
+ ) and
+ msg = "Maybe you're using the function's return value incorrectly."
+ )
+ )
+ )
+}
+
+/** Holds if detecting erroneous situations of working with multibyte characters. */
+predicate findUseMultibyteCharacter(Expr exp, string msg) {
+ exists(ArrayType arrayType, ArrayExpr arrayExpr |
+ arrayExpr = exp and
+ arrayExpr.getArrayBase().getType() = arrayType and
+ (
+ exists(AssignExpr assZero, SizeofExprOperator sizeofArray, Expr oneValue |
+ oneValue.getValue() = "1" and
+ sizeofArray.getExprOperand().getType() = arrayType and
+ assZero.getLValue() = arrayExpr and
+ arrayExpr.getArrayOffset().(SubExpr).hasOperands(sizeofArray, oneValue) and
+ assZero.getRValue().getValue() = "0"
+ ) and
+ arrayType.getArraySize() != arrayType.getByteSize() and
+ msg =
+ "The size of the array element is greater than one byte, so the offset will point outside the array."
+ or
+ exists(FunctionCall mbFunction |
+ (
+ mbFunction.getTarget().getName().matches("_mbs%") or
+ mbFunction.getTarget().getName().matches("mbs%") or
+ mbFunction.getTarget().getName().matches("_mbc%") or
+ mbFunction.getTarget().getName().matches("mbc%")
+ ) and
+ mbFunction.getAnArgument().(VariableAccess).getTarget().getADeclarationEntry().getType() =
+ arrayType
+ ) and
+ exists(Loop loop, SizeofExprOperator sizeofArray, AssignExpr assignExpr |
+ arrayExpr.getEnclosingStmt().getParentStmt*() = loop and
+ sizeofArray.getExprOperand().getType() = arrayType and
+ assignExpr.getLValue() = arrayExpr and
+ loop.getCondition().(LTExpr).getLeftOperand().(VariableAccess).getTarget() =
+ arrayExpr.getArrayOffset().getAChild*().(VariableAccess).getTarget() and
+ loop.getCondition().(LTExpr).getRightOperand() = sizeofArray
+ ) and
+ msg =
+ "This buffer may contain multibyte characters, so attempting to copy may result in part of the last character being lost."
+ )
+ )
+ or
+ exists(FunctionCall mbccpy, Loop loop, SizeofExprOperator sizeofOp |
+ mbccpy.getTarget().hasName("_mbccpy") and
+ mbccpy.getArgument(0) = exp and
+ exp.getEnclosingStmt().getParentStmt*() = loop and
+ sizeofOp.getExprOperand().getType() =
+ exp.getAChild*().(VariableAccess).getTarget().getADeclarationEntry().getType() and
+ loop.getCondition().(LTExpr).getLeftOperand().(VariableAccess).getTarget() =
+ exp.getAChild*().(VariableAccess).getTarget() and
+ loop.getCondition().(LTExpr).getRightOperand() = sizeofOp and
+ msg =
+ "This buffer may contain multibyte characters, so an attempt to copy may result in an overflow."
+ )
+}
+
+/** Holds if erroneous situations of using functions `MultiByteToWideChar` and `WideCharToMultiByte` or `mbstowcs` and `_mbstowcs_l` and `mbsrtowcs` are detected. */
+predicate findUseStringConversion(
+ Expr exp, string msg, int posBufSrc, int posBufDst, int posSizeDst, string nameCalls
+) {
+ exists(FunctionCall fc |
+ fc = exp and
+ posBufSrc in [0 .. fc.getNumberOfArguments() - 1] and
+ posSizeDst in [0 .. fc.getNumberOfArguments() - 1] and
+ (
+ fc.getTarget().hasName(nameCalls) and
+ (
+ globalValueNumber(fc.getArgument(posBufDst)) = globalValueNumber(fc.getArgument(posBufSrc)) and
+ msg =
+ "According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible."
+ or
+ exists(ArrayType arrayDst |
+ fc.getArgument(posBufDst).(VariableAccess).getTarget().getADeclarationEntry().getType() =
+ arrayDst and
+ fc.getArgument(posSizeDst).getValue().toInt() >= arrayDst.getArraySize() and
+ not exists(AssignExpr assZero |
+ assZero.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() =
+ fc.getArgument(posBufDst).(VariableAccess).getTarget() and
+ assZero.getRValue().getValue() = "0"
+ ) and
+ not exists(Expr someExp, FunctionCall checkSize |
+ checkSize.getASuccessor*() = fc and
+ checkSize.getTarget().hasName(nameCalls) and
+ checkSize.getArgument(posSizeDst).getValue() = "0" and
+ globalValueNumber(checkSize) = globalValueNumber(someExp) and
+ someExp.getEnclosingStmt().getParentStmt*() instanceof IfStmt
+ ) and
+ exprMayBeString(fc.getArgument(posBufDst)) and
+ msg =
+ "According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible."
+ )
+ or
+ exists(FunctionCall allocMem |
+ allocMem.getTarget().hasName(["calloc", "malloc"]) and
+ globalValueNumber(fc.getArgument(posBufDst)) = globalValueNumber(allocMem) and
+ (
+ allocMem.getArgument(allocMem.getNumberOfArguments() - 1).getValue() = "1" or
+ not exists(SizeofOperator sizeofOperator |
+ globalValueNumber(allocMem
+ .getArgument(allocMem.getNumberOfArguments() - 1)
+ .getAChild*()) = globalValueNumber(sizeofOperator)
+ )
+ ) and
+ msg =
+ "The buffer destination has a type other than char, you need to take this into account when allocating memory."
+ )
+ or
+ fc.getArgument(posBufDst).getValue() = "0" and
+ fc.getArgument(posSizeDst).getValue() != "0" and
+ msg =
+ "If the destination buffer is NULL and its size is not 0, then undefined behavior is possible."
+ )
+ )
+ )
+}
+
+from Expr exp, string msg
+where
+ findUseCharacterConversion(exp, msg) or
+ findUseMultibyteCharacter(exp, msg) or
+ findUseStringConversion(exp, msg, 1, 0, 2, ["mbstowcs", "_mbstowcs_l", "mbsrtowcs"]) or
+ findUseStringConversion(exp, msg, 2, 4, 5, ["MultiByteToWideChar", "WideCharToMultiByte"])
+select exp, msg
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp
index ca8d8dfaf22..1daebb58b3c 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp
@@ -27,6 +27,9 @@ groups, and finally set the target user.
+CERT C Coding Standard:
+POS36-C. Observe correct revocation order while relinquishing privileges.
+
CERT C Coding Standard:
POS37-C. Ensure that privilege relinquishment is successful.
diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml
index a373e4717d8..03b90cb3668 100644
--- a/cpp/ql/src/qlpack.yml
+++ b/cpp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-queries
-version: 0.2.0-dev
+version: 0.3.2-dev
groups:
- cpp
- queries
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.expected
new file mode 100644
index 00000000000..6766b6c46a4
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.expected
@@ -0,0 +1,26 @@
+| test1.cpp:28:5:28:23 | call to WideCharToMultiByte | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. |
+| test1.cpp:29:5:29:23 | call to MultiByteToWideChar | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. |
+| test1.cpp:45:3:45:21 | call to WideCharToMultiByte | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. |
+| test1.cpp:58:3:58:21 | call to MultiByteToWideChar | The buffer destination has a type other than char, you need to take this into account when allocating memory. |
+| test1.cpp:70:3:70:21 | call to MultiByteToWideChar | The buffer destination has a type other than char, you need to take this into account when allocating memory. |
+| test1.cpp:76:10:76:28 | call to WideCharToMultiByte | If the destination buffer is NULL and its size is not 0, then undefined behavior is possible. |
+| test1.cpp:93:5:93:23 | call to WideCharToMultiByte | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. |
+| test2.cpp:15:5:15:12 | call to mbstowcs | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. |
+| test2.cpp:17:5:17:15 | call to _mbstowcs_l | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. |
+| test2.cpp:19:5:19:13 | call to mbsrtowcs | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. |
+| test2.cpp:35:3:35:10 | call to mbstowcs | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. |
+| test2.cpp:48:3:48:10 | call to mbstowcs | The buffer destination has a type other than char, you need to take this into account when allocating memory. |
+| test2.cpp:60:3:60:10 | call to mbstowcs | The buffer destination has a type other than char, you need to take this into account when allocating memory. |
+| test2.cpp:66:10:66:17 | call to mbstowcs | If the destination buffer is NULL and its size is not 0, then undefined behavior is possible. |
+| test2.cpp:80:3:80:10 | call to mbstowcs | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. |
+| test3.cpp:16:5:16:13 | access to array | This buffer may contain multibyte characters, so attempting to copy may result in part of the last character being lost. |
+| test3.cpp:36:13:36:18 | ... + ... | This buffer may contain multibyte characters, so an attempt to copy may result in an overflow. |
+| test3.cpp:47:3:47:24 | access to array | The size of the array element is greater than one byte, so the offset will point outside the array. |
+| test.cpp:66:27:66:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. |
+| test.cpp:76:27:76:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. |
+| test.cpp:106:11:106:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. |
+| test.cpp:123:11:123:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. |
+| test.cpp:140:11:140:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. |
+| test.cpp:158:11:158:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. |
+| test.cpp:181:11:181:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. |
+| test.cpp:197:11:197:16 | call to mbtowc | Maybe you're using the function's return value incorrectly. |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.qlref
new file mode 100644
index 00000000000..228684a4e25
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp
new file mode 100644
index 00000000000..b5e8096af2a
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp
@@ -0,0 +1,206 @@
+typedef unsigned long size_t;
+#define MB_CUR_MAX 6
+#define MB_LEN_MAX 16
+int mbtowc(wchar_t *out, const char *in, size_t size);
+int wprintf (const wchar_t* format, ...);
+int strlen( const char * string );
+int checkErrors();
+
+void goodTest0()
+{
+ char * ptr = "123456789";
+ int ret;
+ int len;
+ len = 9;
+ for (wchar_t wc; (ret = mbtowc(&wc, ptr, len)) > 0; len-=ret) { // GOOD
+ wprintf(L"%lc", wc);
+ ptr += ret;
+ }
+}
+void goodTest1(const char* ptr)
+{
+ int ret;
+ int len;
+ len = strlen(ptr);
+ for (wchar_t wc; (ret = mbtowc(&wc, ptr, len)) > 0; len-=ret) { // GOOD
+ wprintf(L"%lc", wc);
+ ptr += ret;
+ }
+}
+void goodTest2(char* ptr)
+{
+ int ret;
+ ptr[10]=0;
+ int len = 9;
+ for (wchar_t wc; (ret = mbtowc(&wc, ptr, 16)) > 0; len-=ret) { // GOOD
+ wprintf(L"%lc", wc);
+ ptr += ret;
+ }
+}
+
+void goodTest3(const char* ptr)
+{
+ int ret;
+ int len;
+ len = strlen(ptr);
+ for (wchar_t wc; (ret = mbtowc(&wc, ptr, MB_CUR_MAX)) > 0; len-=ret) { // GOOD
+ wprintf(L"%lc", wc);
+ ptr += ret;
+ }
+}
+void goodTest4(const char* ptr)
+{
+ int ret;
+ int len;
+ len = strlen(ptr);
+ for (wchar_t wc; (ret = mbtowc(&wc, ptr, MB_LEN_MAX)) > 0; len-=ret) { // GOOD
+ wprintf(L"%lc", wc);
+ ptr += ret;
+ }
+}
+void badTest1(const char* ptr)
+{
+ int ret;
+ int len;
+ len = strlen(ptr);
+ for (wchar_t wc; (ret = mbtowc(&wc, ptr, 4)) > 0; len-=ret) { // BAD:we can get unpredictable results
+ wprintf(L"%lc", wc);
+ ptr += ret;
+ }
+}
+void badTest2(const char* ptr)
+{
+ int ret;
+ int len;
+ len = strlen(ptr);
+ for (wchar_t wc; (ret = mbtowc(&wc, ptr, sizeof(wchar_t))) > 0; len-=ret) { // BAD:we can get unpredictable results
+ wprintf(L"%lc", wc);
+ ptr += ret;
+ }
+}
+
+void goodTest5(const char* ptr,wchar_t *wc,int wc_len)
+{
+ int ret;
+ int len;
+ len = wc_len;
+ while (*ptr && len > 0) {
+ ret = mbtowc(wc, ptr, len); // GOOD
+ if (ret <0)
+ break;
+ if (ret == 0 || ret > len)
+ break;
+ len-=ret;
+ ptr+=ret;
+ wc++;
+ }
+}
+
+void badTest3(const char* ptr,int wc_len)
+{
+ int ret;
+ int len;
+ len = wc_len;
+ wchar_t *wc = new wchar_t[wc_len];
+ while (*ptr && len > 0) {
+ ret = mbtowc(wc, ptr, MB_CUR_MAX); // BAD
+ if (ret <0)
+ break;
+ if (ret == 0 || ret > len)
+ break;
+ len-=ret;
+ ptr+=ret;
+ wc++;
+ }
+}
+void badTest4(const char* ptr,int wc_len)
+{
+ int ret;
+ int len;
+ len = wc_len;
+ wchar_t *wc = new wchar_t[wc_len];
+ while (*ptr && len > 0) {
+ ret = mbtowc(wc, ptr, 16); // BAD
+ if (ret <0)
+ break;
+ if (ret == 0 || ret > len)
+ break;
+ len-=ret;
+ ptr+=ret;
+ wc++;
+ }
+}
+void badTest5(const char* ptr,int wc_len)
+{
+ int ret;
+ int len;
+ len = wc_len;
+ wchar_t *wc = new wchar_t[wc_len];
+ while (*ptr && len > 0) {
+ ret = mbtowc(wc, ptr, sizeof(wchar_t)); // BAD
+ if (ret <0)
+ break;
+ if (ret == 0 || ret > len)
+ break;
+ len-=ret;
+ ptr+=ret;
+ wc++;
+ }
+}
+
+void badTest6(const char* ptr,int wc_len)
+{
+ int ret;
+ int len;
+ len = wc_len;
+ wchar_t *wc = new wchar_t[wc_len];
+ while (*ptr && wc_len > 0) {
+ ret = mbtowc(wc, ptr, wc_len); // BAD
+ if (ret <0)
+ if (checkErrors()) {
+ ++ptr;
+ --len;
+ continue;
+ } else
+ break;
+ if (ret == 0 || ret > len)
+ break;
+ wc_len--;
+ len-=ret;
+ wc++;
+ ptr+=ret;
+ }
+}
+void badTest7(const char* ptr,int wc_len)
+{
+ int ret;
+ int len;
+ len = wc_len;
+ wchar_t *wc = new wchar_t[wc_len];
+ while (*ptr && wc_len > 0) {
+ ret = mbtowc(wc, ptr, len); // BAD
+ if (ret <0)
+ break;
+ if (ret == 0 || ret > len)
+ break;
+ len--;
+ wc++;
+ ptr+=ret;
+ }
+}
+void badTest8(const char* ptr,wchar_t *wc)
+{
+ int ret;
+ int len;
+ len = strlen(ptr);
+ while (*ptr && len > 0) {
+ ret = mbtowc(wc, ptr, len); // BAD
+ if (ret <0)
+ break;
+ if (ret == 0 || ret > len)
+ break;
+ len-=ret;
+ ptr++;
+ wc+=ret;
+ }
+}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test1.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test1.cpp
new file mode 100644
index 00000000000..828b91a44f1
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test1.cpp
@@ -0,0 +1,95 @@
+#define CP_ACP 1
+#define CP_UTF8 1
+#define WC_COMPOSITECHECK 1
+#define NULL 0
+typedef unsigned int UINT;
+typedef unsigned long DWORD, *PDWORD, *LPDWORD;
+typedef char CHAR;
+#define CONST const
+typedef wchar_t WCHAR;
+
+typedef CHAR *LPSTR;
+typedef CONST CHAR *LPCSTR;
+typedef CONST WCHAR *LPCWSTR;
+
+typedef int BOOL;
+typedef BOOL *LPBOOL;
+
+
+int WideCharToMultiByte(UINT CodePage,DWORD dwFlags,LPCWSTR lpWideCharStr,int cchWideChar,LPSTR lpMultiByteStr,int cbMultiByte,LPCWSTR lpDefaultChar,LPBOOL lpUsedDefaultChar);
+int MultiByteToWideChar(UINT CodePage,DWORD dwFlags,LPCSTR lpMultiByteStr,int cbMultiByte,LPCWSTR lpWideCharStr,int cchWideChar);
+
+int printf ( const char * format, ... );
+typedef unsigned int size_t;
+void* calloc (size_t num, size_t size);
+void* malloc (size_t size);
+
+void badTest1(void *src, int size) {
+ WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, (LPSTR)src, size, 0, 0); // BAD
+ MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, (LPCWSTR)src, 30); // BAD
+}
+void goodTest2(){
+ wchar_t src[] = L"0123456789ABCDEF";
+ char dst[16];
+ int res = WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, 16, NULL, NULL); // GOOD
+ if (res == sizeof(dst)) {
+ dst[res-1] = NULL;
+ } else {
+ dst[res] = NULL;
+ }
+ printf("%s\n", dst);
+}
+void badTest2(){
+ wchar_t src[] = L"0123456789ABCDEF";
+ char dst[16];
+ WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, 16, NULL, NULL); // BAD
+ printf("%s\n", dst);
+}
+void goodTest3(){
+ char src[] = "0123456789ABCDEF";
+ int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0);
+ wchar_t * dst = (wchar_t*)calloc(size + 1, sizeof(wchar_t));
+ MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // GOOD
+}
+void badTest3(){
+ char src[] = "0123456789ABCDEF";
+ int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0);
+ wchar_t * dst = (wchar_t*)calloc(size + 1, 1);
+ MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // BAD
+}
+void goodTest4(){
+ char src[] = "0123456789ABCDEF";
+ int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0);
+ wchar_t * dst = (wchar_t*)malloc((size + 1)*sizeof(wchar_t));
+ MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // GOOD
+}
+void badTest4(){
+ char src[] = "0123456789ABCDEF";
+ int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0);
+ wchar_t * dst = (wchar_t*)malloc(size + 1);
+ MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // BAD
+}
+int goodTest5(void *src){
+ return WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, 0, 0, 0, 0); // GOOD
+}
+int badTest5 (void *src) {
+ return WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, 0, 3, 0, 0); // BAD
+}
+void goodTest6(WCHAR *src)
+{
+ int size;
+ char dst[5] ="";
+ size = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, src, -1, dst, 0, 0, 0);
+ if(size>=sizeof(dst)){
+ printf("buffer size error\n");
+ return;
+ }
+ WideCharToMultiByte(CP_ACP, 0, src, -1, dst, sizeof(dst), 0, 0); // GOOD
+ printf("%s\n", dst);
+}
+void badTest6(WCHAR *src)
+{
+ char dst[5] ="";
+ WideCharToMultiByte(CP_ACP, 0, src, -1, dst, 260, 0, 0); // BAD
+ printf("%s\n", dst);
+}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test2.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test2.cpp
new file mode 100644
index 00000000000..99dc3e47e5b
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test2.cpp
@@ -0,0 +1,82 @@
+#define NULL 0
+typedef unsigned int size_t;
+struct mbstate_t{};
+struct _locale_t{};
+int printf ( const char * format, ... );
+void* calloc (size_t num, size_t size);
+void* malloc (size_t size);
+
+size_t mbstowcs(wchar_t *wcstr,const char *mbstr,size_t count);
+size_t _mbstowcs_l(wchar_t *wcstr,const char *mbstr,size_t count, _locale_t locale);
+size_t mbsrtowcs(wchar_t *wcstr,const char *mbstr,size_t count, mbstate_t *mbstate);
+
+
+void badTest1(void *src, int size) {
+ mbstowcs((wchar_t*)src,(char*)src,size); // BAD
+ _locale_t locale;
+ _mbstowcs_l((wchar_t*)src,(char*)src,size,locale); // BAD
+ mbstate_t *mbstate;
+ mbsrtowcs((wchar_t*)src,(char*)src,size,mbstate); // BAD
+}
+void goodTest2(){
+ char src[] = "0123456789ABCDEF";
+ wchar_t dst[16];
+ int res = mbstowcs(dst, src,16); // GOOD
+ if (res == sizeof(dst)) {
+ dst[res-1] = NULL;
+ } else {
+ dst[res] = NULL;
+ }
+ printf("%s\n", dst);
+}
+void badTest2(){
+ char src[] = "0123456789ABCDEF";
+ wchar_t dst[16];
+ mbstowcs(dst, src,16); // BAD
+ printf("%s\n", dst);
+}
+void goodTest3(){
+ char src[] = "0123456789ABCDEF";
+ int size = mbstowcs(NULL, src,NULL);
+ wchar_t * dst = (wchar_t*)calloc(size + 1, sizeof(wchar_t));
+ mbstowcs(dst, src,size+1); // GOOD
+}
+void badTest3(){
+ char src[] = "0123456789ABCDEF";
+ int size = mbstowcs(NULL, src,NULL);
+ wchar_t * dst = (wchar_t*)calloc(size + 1, 1);
+ mbstowcs(dst, src,size+1); // BAD
+}
+void goodTest4(){
+ char src[] = "0123456789ABCDEF";
+ int size = mbstowcs(NULL, src,NULL);
+ wchar_t * dst = (wchar_t*)malloc((size + 1)*sizeof(wchar_t));
+ mbstowcs(dst, src,size+1); // GOOD
+}
+void badTest4(){
+ char src[] = "0123456789ABCDEF";
+ int size = mbstowcs(NULL, src,NULL);
+ wchar_t * dst = (wchar_t*)malloc(size + 1);
+ mbstowcs(dst, src,size+1); // BAD
+}
+int goodTest5(void *src){
+ return mbstowcs(NULL, (char*)src,NULL); // GOOD
+}
+int badTest5 (void *src) {
+ return mbstowcs(NULL, (char*)src,3); // BAD
+}
+void goodTest6(void *src){
+ wchar_t dst[5];
+ int size = mbstowcs(NULL, (char*)src,NULL);
+ if(size>=sizeof(dst)){
+ printf("buffer size error\n");
+ return;
+ }
+ mbstowcs(dst, (char*)src,sizeof(dst)); // GOOD
+ printf("%s\n", dst);
+}
+void badTest6(void *src){
+ wchar_t dst[5];
+ mbstowcs(dst, (char*)src,260); // BAD
+ printf("%s\n", dst);
+}
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test3.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test3.cpp
new file mode 100644
index 00000000000..e37052e839b
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test3.cpp
@@ -0,0 +1,48 @@
+#define NULL 0
+typedef unsigned int size_t;
+
+unsigned char * _mbsnbcpy(unsigned char * strDest,const unsigned char * strSource,size_t count);
+size_t _mbclen(const unsigned char *c);
+void _mbccpy(unsigned char *dest,const unsigned char *src);
+unsigned char *_mbsinc(const unsigned char *current);
+void goodTest1(unsigned char *src){
+ unsigned char dst[50];
+ _mbsnbcpy(dst,src,sizeof(dst)); // GOOD
+}
+size_t badTest1(unsigned char *src){
+ int cb = 0;
+ unsigned char dst[50];
+ while( cb < sizeof(dst) )
+ dst[cb++]=*src++; // BAD
+ return _mbclen(dst);
+}
+void goodTest2(unsigned char *src){
+
+ int cb = 0;
+ unsigned char dst[50];
+ while( (cb + _mbclen(src)) <= sizeof(dst) )
+ {
+ _mbccpy(dst+cb,src); // GOOD
+ cb+=_mbclen(src);
+ src=_mbsinc(src);
+ }
+}
+void badTest2(unsigned char *src){
+
+ int cb = 0;
+ unsigned char dst[50];
+ while( cb < sizeof(dst) )
+ {
+ _mbccpy(dst+cb,src); // BAD
+ cb+=_mbclen(src);
+ src=_mbsinc(src);
+ }
+}
+void goodTest3(){
+ wchar_t name[50];
+ name[sizeof(name) / sizeof(*name) - 1] = L'\0'; // GOOD
+}
+void badTest3(){
+ wchar_t name[50];
+ name[sizeof(name) - 1] = L'\0'; // BAD
+}
diff --git a/cpp/ql/test/library-tests/builtins/edg/edg.c b/cpp/ql/test/library-tests/builtins/edg/edg.c
index d1a78435317..f1f0f0f1375 100644
--- a/cpp/ql/test/library-tests/builtins/edg/edg.c
+++ b/cpp/ql/test/library-tests/builtins/edg/edg.c
@@ -1,4 +1,4 @@
-
+// semmle-extractor-options: --clang
struct mystruct {
int f1;
int f2;
@@ -13,3 +13,6 @@ void f(void) {
int i2 = edg_offsetof(struct mystruct,f2);
}
+void g(void) {
+ double f = __builtin_bit_cast(double,42l);
+}
diff --git a/cpp/ql/test/library-tests/builtins/edg/expr.expected b/cpp/ql/test/library-tests/builtins/edg/expr.expected
index 0969dc1e217..5ab0747ecc7 100644
--- a/cpp/ql/test/library-tests/builtins/edg/expr.expected
+++ b/cpp/ql/test/library-tests/builtins/edg/expr.expected
@@ -13,3 +13,6 @@
| edg.c:13:14:13:45 | (size_t)... | 0 | 0 |
| edg.c:13:14:13:45 | __INTADDR__ | 1 | 1 |
| edg.c:13:43:13:44 | f2 | 0 | 0 |
+| edg.c:17:16:17:45 | __builtin_bit_cast | 1 | 1 |
+| edg.c:17:16:17:45 | double | 0 | 0 |
+| edg.c:17:42:17:44 | 42 | 1 | 1 |
diff --git a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected
index 47918496198..a19d917aaac 100644
--- a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected
+++ b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected
@@ -296,3 +296,20 @@
| ms.cpp:255:24:255:43 | a_struct | | |
| ms.cpp:256:24:256:49 | __is_final | a_final_struct | 1 |
| ms.cpp:256:24:256:49 | a_final_struct | | |
+| ms.cpp:258:29:258:62 | __is_assignable | a_struct,a_struct | 1 |
+| ms.cpp:258:29:258:62 | a_struct | | |
+| ms.cpp:258:29:258:62 | a_struct | | |
+| ms.cpp:259:29:259:59 | __is_assignable | a_struct,empty | 0 |
+| ms.cpp:259:29:259:59 | a_struct | | |
+| ms.cpp:259:29:259:59 | empty | | |
+| ms.cpp:260:29:260:57 | __is_assignable | a_struct,int | 0 |
+| ms.cpp:260:29:260:57 | a_struct | | |
+| ms.cpp:260:29:260:57 | int | | |
+| ms.cpp:262:28:262:51 | __is_aggregate | a_struct | 1 |
+| ms.cpp:262:28:262:51 | a_struct | | |
+| ms.cpp:263:28:263:46 | __is_aggregate | int | 0 |
+| ms.cpp:263:28:263:46 | int | | |
+| ms.cpp:265:49:265:88 | __has_unique_object_representations | int | 1 |
+| ms.cpp:265:49:265:88 | int | | |
+| ms.cpp:266:49:266:90 | __has_unique_object_representations | float | 0 |
+| ms.cpp:266:49:266:90 | float | | |
diff --git a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp
index 742f95faf07..91d6245cc35 100644
--- a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp
+++ b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp
@@ -254,5 +254,14 @@ void f(void) {
bool b_is_final1 = __is_final(a_struct);
bool b_is_final2 = __is_final(a_final_struct);
-}
+ bool b_is_assignable1 = __is_assignable(a_struct,a_struct);
+ bool b_is_assignable2 = __is_assignable(a_struct,empty);
+ bool b_is_assignable3 = __is_assignable(a_struct,int);
+
+ bool b_is_aggregate1 = __is_aggregate(a_struct);
+ bool b_is_aggregate2 = __is_aggregate(int);
+
+ bool b_has_unique_object_representations1 = __has_unique_object_representations(int);
+ bool b_has_unique_object_representations2 = __has_unique_object_representations(float);
+}
diff --git a/cpp/ql/test/library-tests/constants/strlen/expr.expected b/cpp/ql/test/library-tests/constants/strlen/expr.expected
index 570ac1317f4..3564cedca30 100644
--- a/cpp/ql/test/library-tests/constants/strlen/expr.expected
+++ b/cpp/ql/test/library-tests/constants/strlen/expr.expected
@@ -1,3 +1,4 @@
+| strlen.cpp:7:49:7:49 | 1 |
| strlen.cpp:11:39:11:48 | array to pointer conversion |
| strlen.cpp:11:39:11:48 | file.ext |
| strlen.cpp:12:35:12:40 | call to strlen |
diff --git a/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected b/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected
new file mode 100644
index 00000000000..bcf301ba47b
--- /dev/null
+++ b/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected
@@ -0,0 +1,20 @@
+| test.cpp:9:9:9:9 | v | test.cpp:5:13:5:13 | v | is not null | is valid |
+| test.cpp:10:9:10:10 | ! ... | test.cpp:5:13:5:13 | v | is null | is not valid |
+| test.cpp:11:9:11:14 | ... == ... | test.cpp:5:13:5:13 | v | is null | is not valid |
+| test.cpp:12:9:12:17 | ... == ... | test.cpp:5:13:5:13 | v | is not null | is valid |
+| test.cpp:13:9:13:14 | ... != ... | test.cpp:5:13:5:13 | v | is not null | is valid |
+| test.cpp:14:9:14:17 | ... != ... | test.cpp:5:13:5:13 | v | is null | is not valid |
+| test.cpp:15:8:15:23 | call to __builtin_expect | test.cpp:5:13:5:13 | v | is not null | is valid |
+| test.cpp:16:8:16:23 | call to __builtin_expect | test.cpp:5:13:5:13 | v | is null | is not valid |
+| test.cpp:17:9:17:17 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is valid |
+| test.cpp:18:9:18:17 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is valid |
+| test.cpp:19:9:19:18 | ... && ... | test.cpp:5:13:5:13 | v | is null | is not valid |
+| test.cpp:20:9:20:18 | ... && ... | test.cpp:5:13:5:13 | v | is null | is not valid |
+| test.cpp:21:9:21:14 | ... = ... | test.cpp:5:13:5:13 | v | is null | is not valid |
+| test.cpp:21:9:21:14 | ... = ... | test.cpp:7:10:7:10 | b | is not null | is valid |
+| test.cpp:22:9:22:14 | ... = ... | test.cpp:5:13:5:13 | v | is not null | is not valid |
+| test.cpp:22:9:22:14 | ... = ... | test.cpp:7:13:7:13 | c | is not null | is not valid |
+| test.cpp:22:17:22:17 | c | test.cpp:7:13:7:13 | c | is not null | is valid |
+| test.cpp:23:21:23:21 | x | test.cpp:23:14:23:14 | x | is not null | is valid |
+| test.cpp:24:9:24:18 | (condition decl) | test.cpp:5:13:5:13 | v | is not null | is not valid |
+| test.cpp:24:9:24:18 | (condition decl) | test.cpp:24:14:24:14 | y | is not null | is valid |
diff --git a/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql b/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql
new file mode 100644
index 00000000000..864fd04f920
--- /dev/null
+++ b/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql
@@ -0,0 +1,8 @@
+import cpp
+
+from AnalysedExpr a, LocalScopeVariable v, string isNullCheck, string isValidCheck
+where
+ v.getAnAccess().getEnclosingStmt() = a.getParent() and
+ (if a.isNullCheck(v) then isNullCheck = "is null" else isNullCheck = "is not null") and
+ (if a.isValidCheck(v) then isValidCheck = "is valid" else isValidCheck = "is not valid")
+select a, v, isNullCheck, isValidCheck
diff --git a/cpp/ql/test/library-tests/controlflow/nullness/test.cpp b/cpp/ql/test/library-tests/controlflow/nullness/test.cpp
new file mode 100644
index 00000000000..407753be17a
--- /dev/null
+++ b/cpp/ql/test/library-tests/controlflow/nullness/test.cpp
@@ -0,0 +1,25 @@
+// semmle-extractor-options: -std=c++17
+
+long __builtin_expect(long);
+
+void f(int *v) {
+ int *w;
+ bool b, c;
+
+ if (v) {}
+ if (!v) {}
+ if (v == 0) {}
+ if ((!v) == 0) {}
+ if (v != 0) {}
+ if ((!v) != 0) {}
+ if(__builtin_expect((long)v)) {}
+ if(__builtin_expect((long)!v)) {}
+ if (true && v) {}
+ if (v && true) {}
+ if (true && !v) {}
+ if (!v && true) {}
+ if (b = !v) {}
+ if (c = !v; c) {}
+ if (int *x = v; x) {}
+ if (int *y = v) {}
+}
diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
index 5e5c5279f16..18eb893e540 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp
@@ -334,19 +334,19 @@ namespace FlowThroughGlobals {
}
int f() {
- sink(globalVar); // tainted or clean? Not sure.
+ sink(globalVar); // $ ir=333:17 ir=347:17 // tainted or clean? Not sure.
taintGlobal();
- sink(globalVar); // $ MISSING: ast,ir
+ sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast
}
int calledAfterTaint() {
- sink(globalVar); // $ MISSING: ast,ir
+ sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast
}
int taintAndCall() {
globalVar = source();
calledAfterTaint();
- sink(globalVar); // $ ast,ir
+ sink(globalVar); // $ ast ir=333:17 ir=347:17
}
}
diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
index ebbde802ff3..2ae093098d2 100644
--- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
+++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp
@@ -47,14 +47,14 @@ void do_source()
void do_sink()
{
sink(global1);
- sink(global2); // $ MISSING: ast,ir
- sink(global3); // $ MISSING: ast,ir
- sink(global4); // $ MISSING: ast,ir
+ sink(global2); // $ ir MISSING: ast
+ sink(global3); // $ ir MISSING: ast
+ sink(global4); // $ ir MISSING: ast
sink(global5);
sink(global6);
- sink(global7); // $ MISSING: ast,ir
- sink(global8); // $ MISSING: ast,ir
- sink(global9); // $ MISSING: ast,ir
+ sink(global7); // $ ir MISSING: ast
+ sink(global8); // $ ir MISSING: ast
+ sink(global9); // $ ir MISSING: ast
sink(global10);
}
diff --git a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll
index a9167597691..bd77d831cb7 100644
--- a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll
+++ b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll
@@ -15,7 +15,7 @@ predicate locationIsInStandardHeaders(Location loc) {
predicate shouldDumpFunction(Declaration decl) {
not locationIsInStandardHeaders(decl.getLocation()) and
(
- not decl instanceof Variable
+ decl instanceof Function
or
decl.(GlobalOrNamespaceVariable).hasInitializer()
)
diff --git a/cpp/ql/test/library-tests/templates/CPP-203/decls.expected b/cpp/ql/test/library-tests/templates/CPP-203/decls.expected
index b311041021d..33aa6114052 100644
--- a/cpp/ql/test/library-tests/templates/CPP-203/decls.expected
+++ b/cpp/ql/test/library-tests/templates/CPP-203/decls.expected
@@ -15,6 +15,7 @@
| test.cpp:3:8:3:8 | operator= |
| test.cpp:3:8:3:10 | Str |
| test.cpp:3:8:3:10 | Str |
+| test.cpp:7:16:7:16 | T |
| test.cpp:8:11:8:21 | val |
| test.cpp:8:19:8:19 | val |
| test.cpp:10:6:10:6 | f |
diff --git a/cpp/ql/test/library-tests/variables/global/variables.expected b/cpp/ql/test/library-tests/variables/global/variables.expected
index d66899e62fa..9d022a98264 100644
--- a/cpp/ql/test/library-tests/variables/global/variables.expected
+++ b/cpp/ql/test/library-tests/variables/global/variables.expected
@@ -4,11 +4,7 @@
| c.c:6:5:6:6 | ls | array of 4 {int} | 1 |
| c.c:8:5:8:7 | iss | array of 4 {array of 2 {int}} | 1 |
| c.c:12:11:12:11 | i | typedef {int} as "int_alias" | 1 |
-| c.h:4:12:4:13 | ks | array of {int} | 1 |
-| c.h:8:12:8:14 | iss | array of {array of 2 {int}} | 1 |
-| c.h:10:12:10:12 | i | int | 1 |
| d.cpp:3:7:3:8 | xs | array of {int} | 1 |
-| d.h:3:14:3:15 | xs | array of 2 {int} | 1 |
| file://:0:0:0:0 | (unnamed parameter 0) | reference to {const {struct __va_list_tag}} | 1 |
| file://:0:0:0:0 | (unnamed parameter 0) | rvalue reference to {struct __va_list_tag} | 1 |
| file://:0:0:0:0 | fp_offset | unsigned int | 1 |
diff --git a/cpp/ql/test/library-tests/vector_types/builtin_ops.expected b/cpp/ql/test/library-tests/vector_types/builtin_ops.expected
index 2c0dd9d0017..756ca2f1c17 100644
--- a/cpp/ql/test/library-tests/vector_types/builtin_ops.expected
+++ b/cpp/ql/test/library-tests/vector_types/builtin_ops.expected
@@ -1,2 +1,4 @@
+| vector_types2.cpp:10:15:10:42 | __builtin_shuffle |
+| vector_types2.cpp:11:15:11:45 | __builtin_shuffle |
| vector_types.cpp:31:13:31:49 | __builtin_shufflevector |
| vector_types.cpp:58:10:58:52 | __builtin_convertvector |
diff --git a/cpp/ql/test/library-tests/vector_types/variables.expected b/cpp/ql/test/library-tests/vector_types/variables.expected
index 2494f192e9a..52fa98dd3f0 100644
--- a/cpp/ql/test/library-tests/vector_types/variables.expected
+++ b/cpp/ql/test/library-tests/vector_types/variables.expected
@@ -13,6 +13,12 @@
| file://:0:0:0:0 | gp_offset | gp_offset | file://:0:0:0:0 | unsigned int | 4 |
| file://:0:0:0:0 | overflow_arg_area | overflow_arg_area | file://:0:0:0:0 | void * | 8 |
| file://:0:0:0:0 | reg_save_area | reg_save_area | file://:0:0:0:0 | void * | 8 |
+| vector_types2.cpp:5:7:5:7 | a | a | vector_types2.cpp:2:13:2:15 | v4i | 16 |
+| vector_types2.cpp:6:7:6:7 | b | b | vector_types2.cpp:2:13:2:15 | v4i | 16 |
+| vector_types2.cpp:7:7:7:12 | mask_1 | mask_1 | vector_types2.cpp:2:13:2:15 | v4i | 16 |
+| vector_types2.cpp:8:7:8:12 | mask_2 | mask_2 | vector_types2.cpp:2:13:2:15 | v4i | 16 |
+| vector_types2.cpp:10:7:10:11 | res_1 | res_1 | vector_types2.cpp:2:13:2:15 | v4i | 16 |
+| vector_types2.cpp:11:7:11:11 | res_2 | res_2 | vector_types2.cpp:2:13:2:15 | v4i | 16 |
| vector_types.cpp:9:21:9:21 | x | x | vector_types.cpp:6:15:6:17 | v4f | 16 |
| vector_types.cpp:14:18:14:20 | lhs | lhs | vector_types.cpp:6:15:6:17 | v4f | 16 |
| vector_types.cpp:14:27:14:29 | rhs | rhs | vector_types.cpp:6:15:6:17 | v4f | 16 |
diff --git a/cpp/ql/test/library-tests/vector_types/vector_types2.cpp b/cpp/ql/test/library-tests/vector_types/vector_types2.cpp
new file mode 100644
index 00000000000..d4233b28890
--- /dev/null
+++ b/cpp/ql/test/library-tests/vector_types/vector_types2.cpp
@@ -0,0 +1,12 @@
+// semmle-extractor-options: --gnu --gnu_version 80000
+typedef int v4i __attribute__((vector_size (16)));
+
+void f() {
+ v4i a = {1,2,3,4};
+ v4i b = {5,6,7,8};
+ v4i mask_1 = {3,0,1,2};
+ v4i mask_2 = {3,5,4,2};
+
+ v4i res_1 = __builtin_shuffle(a, mask_1);
+ v4i res_2 = __builtin_shuffle(a, b, mask_2);
+}
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.expected b/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.expected
similarity index 100%
rename from cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.expected
rename to cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.expected
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.qlref b/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.qlref
similarity index 100%
rename from cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.qlref
rename to cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.qlref
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 6b8a59793a3..8f9d91fc1ad 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
@@ -100,12 +100,6 @@ edges
| 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:225:14:225:15 | px | test.cpp:226:10:226:11 | Load |
-| test.cpp:226:10:226:11 | Load | test.cpp:226:10:226:11 | px |
-| test.cpp:226:10:226:11 | px | test.cpp:226:10:226:11 | StoreValue |
-| test.cpp:231:16:231:17 | & ... | test.cpp:225:14:225:15 | px |
-| test.cpp:231:17:231:17 | Unary | test.cpp:231:16:231:17 | & ... |
-| test.cpp:231:17:231:17 | x | test.cpp:231:17:231:17 | Unary |
nodes
| test.cpp:17:9:17:11 | & ... | semmle.label | & ... |
| test.cpp:17:9:17:11 | StoreValue | semmle.label | StoreValue |
@@ -221,13 +215,6 @@ nodes
| 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 |
-| test.cpp:225:14:225:15 | px | semmle.label | px |
-| test.cpp:226:10:226:11 | Load | semmle.label | Load |
-| test.cpp:226:10:226:11 | StoreValue | semmle.label | StoreValue |
-| test.cpp:226:10:226:11 | px | semmle.label | px |
-| test.cpp:231:16:231:17 | & ... | semmle.label | & ... |
-| test.cpp:231:17:231:17 | Unary | semmle.label | Unary |
-| test.cpp:231:17:231:17 | x | semmle.label | x |
#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 |
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StrncpyFlippedArgs/StrncpyFlippedArgs.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StrncpyFlippedArgs/StrncpyFlippedArgs.expected
index 88a953b0196..1fe46acbdde 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StrncpyFlippedArgs/StrncpyFlippedArgs.expected
+++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StrncpyFlippedArgs/StrncpyFlippedArgs.expected
@@ -1,18 +1,23 @@
| test.c:22:2:22:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
| test.c:33:2:33:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
-| test.cpp:19:2:19:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
-| test.cpp:20:2:20:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
| test.cpp:21:2:21:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
-| test.cpp:30:2:30:8 | call to wcsncpy | Potentially unsafe call to wcsncpy; third argument should be size of destination. |
+| test.cpp:22:2:22:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
+| test.cpp:23:2:23:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
| test.cpp:32:2:32:8 | call to wcsncpy | Potentially unsafe call to wcsncpy; third argument should be size of destination. |
-| test.cpp:33:2:33:8 | call to wcsncpy | Potentially unsafe call to wcsncpy; third argument should be size of destination. |
| test.cpp:34:2:34:8 | call to wcsncpy | Potentially unsafe call to wcsncpy; third argument should be size of destination. |
| test.cpp:35:2:35:8 | call to wcsncpy | Potentially unsafe call to wcsncpy; third argument should be size of destination. |
-| test.cpp:45:2:45:9 | call to strcpy_s | Potentially unsafe call to strcpy_s; second argument should be size of destination. |
-| test.cpp:46:2:46:9 | call to strcpy_s | Potentially unsafe call to strcpy_s; second argument should be size of destination. |
+| test.cpp:36:2:36:8 | call to wcsncpy | Potentially unsafe call to wcsncpy; third argument should be size of destination. |
+| test.cpp:37:2:37:8 | call to wcsncpy | Potentially unsafe call to wcsncpy; third argument should be size of destination. |
| test.cpp:47:2:47:9 | call to strcpy_s | Potentially unsafe call to strcpy_s; second argument should be size of destination. |
-| test.cpp:60:3:60:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
-| test.cpp:63:3:63:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
-| test.cpp:68:2:68:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
-| test.cpp:79:3:79:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
-| test.cpp:82:3:82:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
+| test.cpp:48:2:48:9 | call to strcpy_s | Potentially unsafe call to strcpy_s; second argument should be size of destination. |
+| test.cpp:49:2:49:9 | call to strcpy_s | Potentially unsafe call to strcpy_s; second argument should be size of destination. |
+| test.cpp:62:3:62:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
+| test.cpp:65:3:65:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
+| test.cpp:70:2:70:8 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
+| test.cpp:81:3:81:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
+| test.cpp:84:3:84:9 | call to strncpy | Potentially unsafe call to strncpy; third argument should be size of destination. |
+| test.cpp:105:2:105:10 | call to wcsxfrm_l | Potentially unsafe call to wcsxfrm_l; third argument should be size of destination. |
+| test.cpp:107:2:107:10 | call to wcsxfrm_l | Potentially unsafe call to wcsxfrm_l; third argument should be size of destination. |
+| test.cpp:108:2:108:10 | call to wcsxfrm_l | Potentially unsafe call to wcsxfrm_l; third argument should be size of destination. |
+| test.cpp:109:2:109:10 | call to wcsxfrm_l | Potentially unsafe call to wcsxfrm_l; third argument should be size of destination. |
+| test.cpp:110:2:110:10 | call to wcsxfrm_l | Potentially unsafe call to wcsxfrm_l; third argument should be size of destination. |
diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StrncpyFlippedArgs/test.cpp b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StrncpyFlippedArgs/test.cpp
index b31e8762467..ad2e39b748e 100644
--- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StrncpyFlippedArgs/test.cpp
+++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/StrncpyFlippedArgs/test.cpp
@@ -1,9 +1,11 @@
typedef unsigned int size_t;
typedef unsigned int errno_t;
+typedef void *locale_t;
char *strncpy(char *__restrict destination, const char *__restrict source, size_t num);
wchar_t *wcsncpy(wchar_t *__restrict destination, const wchar_t *__restrict source, size_t num);
+size_t wcsxfrm_l(wchar_t *ws1, const wchar_t *ws2, size_t n, locale_t locale);
errno_t strcpy_s(char *strDestination, size_t numberOfElements, const char *strSource);
size_t strlen(const char *str);
@@ -93,3 +95,17 @@ void test8(char x[], char y[]) {
// that it will be a false positive if we report it.
strncpy(x, y, 32);
}
+
+void test9()
+{
+ wchar_t buf1[10];
+ wchar_t buf2[20];
+ const wchar_t *str = L"01234567890123456789";
+
+ wcsxfrm_l(buf1, str, sizeof(buf1), nullptr); // BAD (but not a StrncpyFlippedArgs bug)
+ wcsxfrm_l(buf1, str, sizeof(buf1) / sizeof(wchar_t), nullptr); // GOOD
+ wcsxfrm_l(buf1, str, wcslen(str), nullptr); // BAD
+ wcsxfrm_l(buf1, str, wcslen(str) + 1, nullptr); // BAD
+ wcsxfrm_l(buf1, buf2, sizeof(buf2), nullptr); // BAD
+ wcsxfrm_l(buf1, buf2, sizeof(buf2) / sizeof(wchar_t), nullptr); // BAD
+}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected
index 7cefb7cfafc..885e188be64 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected
@@ -1,4 +1,134 @@
edges
+| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:33:15:33:18 | copy |
+| globalVars.c:8:7:8:10 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:44:15:44:19 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:11:22:11:25 | *argv | globalVars.c:12:2:12:15 | Store |
+| globalVars.c:11:22:11:25 | argv | globalVars.c:12:2:12:15 | Store |
+| globalVars.c:12:2:12:15 | Store | globalVars.c:8:7:8:10 | copy |
+| globalVars.c:15:21:15:23 | val | globalVars.c:16:2:16:12 | Store |
+| globalVars.c:16:2:16:12 | Store | globalVars.c:9:7:9:11 | copy2 |
+| globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection |
+| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | argv |
+| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv |
+| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv |
+| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv indirection |
+| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv indirection |
+| globalVars.c:24:11:24:14 | argv indirection | globalVars.c:11:22:11:25 | *argv |
+| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | (const char *)... |
+| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy |
+| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy indirection |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:19:25:19:27 | *str |
+| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:30:15:30:18 | printWrapper output argument |
+| globalVars.c:30:15:30:18 | printWrapper output argument | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:33:15:33:18 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:35:11:35:14 | copy | globalVars.c:15:21:15:23 | val |
+| globalVars.c:35:11:35:14 | copy | globalVars.c:35:11:35:14 | copy |
+| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | (const char *)... |
+| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 |
+| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:19:25:19:27 | *str |
+| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:41:15:41:19 | printWrapper output argument |
+| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | (const char *)... |
+| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 indirection |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection |
+| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | (const char *)... |
+| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 |
+| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection |
subpaths
+| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection | globalVars.c:30:15:30:18 | printWrapper output argument |
+| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection | globalVars.c:41:15:41:19 | printWrapper output argument |
nodes
+| globalVars.c:8:7:8:10 | copy | semmle.label | copy |
+| globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 |
+| globalVars.c:11:22:11:25 | *argv | semmle.label | *argv |
+| globalVars.c:11:22:11:25 | argv | semmle.label | argv |
+| globalVars.c:12:2:12:15 | Store | semmle.label | Store |
+| globalVars.c:15:21:15:23 | val | semmle.label | val |
+| globalVars.c:16:2:16:12 | Store | semmle.label | Store |
+| globalVars.c:19:25:19:27 | *str | semmle.label | *str |
+| globalVars.c:19:25:19:27 | ReturnIndirection | semmle.label | ReturnIndirection |
+| globalVars.c:24:11:24:14 | argv | semmle.label | argv |
+| globalVars.c:24:11:24:14 | argv | semmle.label | argv |
+| globalVars.c:24:11:24:14 | argv | semmle.label | argv |
+| globalVars.c:24:11:24:14 | argv indirection | semmle.label | argv indirection |
+| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
+| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
+| globalVars.c:27:9:27:12 | copy | semmle.label | copy |
+| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
+| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
+| globalVars.c:30:15:30:18 | copy | semmle.label | copy |
+| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection |
+| globalVars.c:30:15:30:18 | printWrapper output argument | semmle.label | printWrapper output argument |
+| globalVars.c:33:15:33:18 | copy | semmle.label | copy |
+| globalVars.c:35:11:35:14 | copy | semmle.label | copy |
+| globalVars.c:35:11:35:14 | copy | semmle.label | copy |
+| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:41:15:41:19 | printWrapper output argument | semmle.label | printWrapper output argument |
+| globalVars.c:44:15:44:19 | copy2 | semmle.label | copy2 |
+| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... |
+| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 |
+| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection |
+| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection |
#select
+| globalVars.c:27:9:27:12 | copy | globalVars.c:24:11:24:14 | argv | globalVars.c:27:9:27:12 | copy | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | globalVars.c:24:11:24:14 | argv | argv |
+| globalVars.c:30:15:30:18 | copy | globalVars.c:24:11:24:14 | argv | globalVars.c:30:15:30:18 | copy | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(str), which calls printf(format) | globalVars.c:24:11:24:14 | argv | argv |
+| globalVars.c:38:9:38:13 | copy2 | globalVars.c:24:11:24:14 | argv | globalVars.c:38:9:38:13 | copy2 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | globalVars.c:24:11:24:14 | argv | argv |
+| globalVars.c:41:15:41:19 | copy2 | globalVars.c:24:11:24:14 | argv | globalVars.c:41:15:41:19 | copy2 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(str), which calls printf(format) | globalVars.c:24:11:24:14 | argv | argv |
+| globalVars.c:50:9:50:13 | copy2 | globalVars.c:24:11:24:14 | argv | globalVars.c:50:9:50:13 | copy2 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | globalVars.c:24:11:24:14 | argv | argv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected
index 80b195bd0bd..11ec8e849a5 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected
@@ -1,4 +1,8 @@
edges
+| tests2.cpp:50:13:50:19 | global1 | tests2.cpp:82:14:82:20 | global1 |
+| tests2.cpp:50:13:50:19 | global1 | tests2.cpp:82:14:82:20 | global1 |
+| tests2.cpp:50:23:50:43 | Store | tests2.cpp:50:13:50:19 | global1 |
+| tests2.cpp:50:23:50:43 | call to mysql_get_client_info | tests2.cpp:50:23:50:43 | Store |
| tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:26 | (const char *)... |
| tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:26 | (const char *)... |
| tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:30 | (const char *)... |
@@ -6,6 +10,8 @@ edges
| tests2.cpp:78:18:78:38 | call to mysql_get_client_info | tests2.cpp:81:14:81:19 | (const char *)... |
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info |
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info |
+| tests2.cpp:82:14:82:20 | global1 | tests2.cpp:82:14:82:20 | global1 |
+| tests2.cpp:82:14:82:20 | global1 | tests2.cpp:82:14:82:20 | global1 |
| tests2.cpp:91:42:91:45 | str1 | tests2.cpp:93:14:93:17 | str1 |
| tests2.cpp:101:8:101:15 | call to getpwuid | tests2.cpp:102:14:102:15 | pw |
| tests2.cpp:109:3:109:4 | c1 [post update] [ptr] | tests2.cpp:111:14:111:15 | c1 [read] [ptr] |
@@ -23,6 +29,9 @@ edges
| tests_sysconf.cpp:36:21:36:27 | confstr output argument | tests_sysconf.cpp:39:19:39:25 | (const void *)... |
| tests_sysconf.cpp:36:21:36:27 | confstr output argument | tests_sysconf.cpp:39:19:39:25 | pathbuf |
nodes
+| tests2.cpp:50:13:50:19 | global1 | semmle.label | global1 |
+| tests2.cpp:50:23:50:43 | Store | semmle.label | Store |
+| tests2.cpp:50:23:50:43 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv |
| tests2.cpp:63:13:63:26 | (const char *)... | semmle.label | (const char *)... |
@@ -39,6 +48,8 @@ nodes
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info |
| tests2.cpp:81:14:81:19 | (const char *)... | semmle.label | (const char *)... |
+| tests2.cpp:82:14:82:20 | global1 | semmle.label | global1 |
+| tests2.cpp:82:14:82:20 | global1 | semmle.label | global1 |
| tests2.cpp:91:42:91:45 | str1 | semmle.label | str1 |
| tests2.cpp:93:14:93:17 | str1 | semmle.label | str1 |
| tests2.cpp:101:8:101:15 | call to getpwuid | semmle.label | call to getpwuid |
@@ -70,6 +81,7 @@ subpaths
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:81:14:81:19 | (const char *)... | tests2.cpp:78:18:78:38 | call to mysql_get_client_info | tests2.cpp:81:14:81:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:78:18:78:38 | call to mysql_get_client_info | call to mysql_get_client_info |
+| tests2.cpp:82:14:82:20 | global1 | tests2.cpp:50:23:50:43 | call to mysql_get_client_info | tests2.cpp:82:14:82:20 | global1 | This operation exposes system data from $@. | tests2.cpp:50:23:50:43 | call to mysql_get_client_info | call to mysql_get_client_info |
| tests2.cpp:93:14:93:17 | str1 | tests2.cpp:91:42:91:45 | str1 | tests2.cpp:93:14:93:17 | str1 | This operation exposes system data from $@. | tests2.cpp:91:42:91:45 | str1 | str1 |
| tests2.cpp:102:14:102:15 | pw | tests2.cpp:101:8:101:15 | call to getpwuid | tests2.cpp:102:14:102:15 | pw | This operation exposes system data from $@. | tests2.cpp:101:8:101:15 | call to getpwuid | call to getpwuid |
| tests2.cpp:111:14:111:19 | (const char *)... | tests2.cpp:109:12:109:17 | call to getenv | tests2.cpp:111:14:111:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:109:12:109:17 | call to getenv | call to getenv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected
index 62fe44dcc23..ff10a3b9c1c 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected
@@ -5,6 +5,14 @@ edges
| tests.cpp:57:18:57:23 | call to getenv | tests.cpp:57:18:57:39 | (const char_type *)... |
| tests.cpp:58:41:58:46 | call to getenv | tests.cpp:58:41:58:62 | (const char_type *)... |
| tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:64 | (const char *)... |
+| tests.cpp:62:7:62:18 | global_token | tests.cpp:69:17:69:28 | global_token |
+| tests.cpp:62:7:62:18 | global_token | tests.cpp:71:27:71:38 | global_token |
+| tests.cpp:62:7:62:18 | global_token | tests.cpp:71:27:71:38 | global_token |
+| tests.cpp:62:22:62:27 | Store | tests.cpp:62:7:62:18 | global_token |
+| tests.cpp:62:22:62:27 | call to getenv | tests.cpp:62:22:62:27 | Store |
+| tests.cpp:69:17:69:28 | global_token | tests.cpp:73:27:73:31 | maybe |
+| tests.cpp:71:27:71:38 | global_token | tests.cpp:71:27:71:38 | global_token |
+| tests.cpp:71:27:71:38 | global_token | tests.cpp:71:27:71:38 | global_token |
| tests.cpp:86:29:86:31 | *msg | tests.cpp:88:15:88:17 | msg |
| tests.cpp:86:29:86:31 | msg | tests.cpp:88:15:88:17 | msg |
| tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:34 | (const char *)... |
@@ -52,6 +60,13 @@ nodes
| tests.cpp:59:43:59:48 | call to getenv | semmle.label | call to getenv |
| tests.cpp:59:43:59:48 | call to getenv | semmle.label | call to getenv |
| tests.cpp:59:43:59:64 | (const char *)... | semmle.label | (const char *)... |
+| tests.cpp:62:7:62:18 | global_token | semmle.label | global_token |
+| tests.cpp:62:22:62:27 | Store | semmle.label | Store |
+| tests.cpp:62:22:62:27 | call to getenv | semmle.label | call to getenv |
+| tests.cpp:69:17:69:28 | global_token | semmle.label | global_token |
+| tests.cpp:71:27:71:38 | global_token | semmle.label | global_token |
+| tests.cpp:71:27:71:38 | global_token | semmle.label | global_token |
+| tests.cpp:73:27:73:31 | maybe | semmle.label | maybe |
| tests.cpp:86:29:86:31 | *msg | semmle.label | *msg |
| tests.cpp:86:29:86:31 | msg | semmle.label | msg |
| tests.cpp:88:15:88:17 | msg | semmle.label | msg |
@@ -97,6 +112,8 @@ subpaths
| tests.cpp:58:41:58:62 | (const char_type *)... | tests.cpp:58:41:58:46 | call to getenv | tests.cpp:58:41:58:62 | (const char_type *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:58:41:58:46 | call to getenv | call to getenv |
| tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:48 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:59:43:59:48 | call to getenv | call to getenv |
| tests.cpp:59:43:59:64 | (const char *)... | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:64 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:59:43:59:48 | call to getenv | call to getenv |
+| tests.cpp:71:27:71:38 | global_token | tests.cpp:62:22:62:27 | call to getenv | tests.cpp:71:27:71:38 | global_token | This operation potentially exposes sensitive system data from $@. | tests.cpp:62:22:62:27 | call to getenv | call to getenv |
+| tests.cpp:73:27:73:31 | maybe | tests.cpp:62:22:62:27 | call to getenv | tests.cpp:73:27:73:31 | maybe | This operation potentially exposes sensitive system data from $@. | tests.cpp:62:22:62:27 | call to getenv | call to getenv |
| tests.cpp:88:15:88:17 | msg | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:88:15:88:17 | msg | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv |
| tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:18 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv |
| tests.cpp:97:13:97:34 | (const char *)... | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:34 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp
index e61fc582fc3..843d579386b 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp
@@ -68,9 +68,9 @@ void test2(bool cond)
maybe = cond ? global_token : global_other;
- printf("token = '%s'\n", global_token); // BAD: outputs SECRET_TOKEN environment variable [NOT DETECTED]
+ printf("token = '%s'\n", global_token); // BAD: outputs SECRET_TOKEN environment variable
printf("other = '%s'\n", global_other);
- printf("maybe = '%s'\n", maybe); // BAD: may output SECRET_TOKEN environment variable [NOT DETECTED]
+ printf("maybe = '%s'\n", maybe); // BAD: may output SECRET_TOKEN environment variable
}
void test3()
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp
index 763f1ecfafc..e1399eab343 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp
@@ -79,7 +79,7 @@ void test1()
send(sock, mysql_get_client_info(), val(), val()); // BAD
send(sock, buffer, val(), val()); // BAD
- send(sock, global1, val(), val()); // BAD [NOT DETECTED]
+ send(sock, global1, val(), val()); // BAD
send(sock, global2, val(), val()); // GOOD: not system data
}
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected
index a4db6155e31..7403cba893a 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected
@@ -2,11 +2,26 @@ edges
| tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p |
| tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p |
| tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p |
+| tests3.cpp:35:16:35:20 | p_3_3 | tests3.cpp:38:2:38:6 | p_3_3 |
+| tests3.cpp:35:24:35:56 | Store | tests3.cpp:35:16:35:20 | p_3_3 |
+| tests3.cpp:35:24:35:56 | call to createXMLReader | tests3.cpp:35:24:35:56 | Store |
+| tests3.cpp:41:16:41:20 | p_3_4 | tests3.cpp:45:2:45:6 | p_3_4 |
+| tests3.cpp:41:24:41:56 | Store | tests3.cpp:41:16:41:20 | p_3_4 |
+| tests3.cpp:41:24:41:56 | call to createXMLReader | tests3.cpp:41:24:41:56 | Store |
+| tests3.cpp:48:16:48:20 | p_3_5 | tests3.cpp:56:2:56:6 | p_3_5 |
+| tests3.cpp:48:24:48:56 | Store | tests3.cpp:48:16:48:20 | p_3_5 |
+| tests3.cpp:48:24:48:56 | call to createXMLReader | tests3.cpp:48:24:48:56 | Store |
| tests3.cpp:60:21:60:53 | call to createXMLReader | tests3.cpp:63:2:63:2 | p |
| tests3.cpp:67:21:67:53 | call to createXMLReader | tests3.cpp:70:2:70:2 | p |
| tests5.cpp:27:25:27:38 | call to createLSParser | tests5.cpp:29:2:29:2 | p |
| tests5.cpp:40:25:40:38 | call to createLSParser | tests5.cpp:43:2:43:2 | p |
| tests5.cpp:55:25:55:38 | call to createLSParser | tests5.cpp:59:2:59:2 | p |
+| tests5.cpp:63:14:63:17 | g_p1 | tests5.cpp:76:2:76:5 | g_p1 |
+| tests5.cpp:63:21:63:24 | g_p2 | tests5.cpp:77:2:77:5 | g_p2 |
+| tests5.cpp:67:2:67:32 | Store | tests5.cpp:63:14:63:17 | g_p1 |
+| tests5.cpp:67:17:67:30 | call to createLSParser | tests5.cpp:67:2:67:32 | Store |
+| tests5.cpp:70:2:70:32 | Store | tests5.cpp:63:21:63:24 | g_p2 |
+| tests5.cpp:70:17:70:30 | call to createLSParser | tests5.cpp:70:2:70:32 | Store |
| tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p |
| tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p |
| tests5.cpp:83:2:83:2 | p | tests5.cpp:85:2:85:2 | p |
@@ -46,6 +61,18 @@ nodes
| tests2.cpp:37:2:37:2 | p | semmle.label | p |
| tests3.cpp:23:21:23:53 | call to createXMLReader | semmle.label | call to createXMLReader |
| tests3.cpp:25:2:25:2 | p | semmle.label | p |
+| tests3.cpp:35:16:35:20 | p_3_3 | semmle.label | p_3_3 |
+| tests3.cpp:35:24:35:56 | Store | semmle.label | Store |
+| tests3.cpp:35:24:35:56 | call to createXMLReader | semmle.label | call to createXMLReader |
+| tests3.cpp:38:2:38:6 | p_3_3 | semmle.label | p_3_3 |
+| tests3.cpp:41:16:41:20 | p_3_4 | semmle.label | p_3_4 |
+| tests3.cpp:41:24:41:56 | Store | semmle.label | Store |
+| tests3.cpp:41:24:41:56 | call to createXMLReader | semmle.label | call to createXMLReader |
+| tests3.cpp:45:2:45:6 | p_3_4 | semmle.label | p_3_4 |
+| tests3.cpp:48:16:48:20 | p_3_5 | semmle.label | p_3_5 |
+| tests3.cpp:48:24:48:56 | Store | semmle.label | Store |
+| tests3.cpp:48:24:48:56 | call to createXMLReader | semmle.label | call to createXMLReader |
+| tests3.cpp:56:2:56:6 | p_3_5 | semmle.label | p_3_5 |
| tests3.cpp:60:21:60:53 | call to createXMLReader | semmle.label | call to createXMLReader |
| tests3.cpp:63:2:63:2 | p | semmle.label | p |
| tests3.cpp:67:21:67:53 | call to createXMLReader | semmle.label | call to createXMLReader |
@@ -61,6 +88,14 @@ nodes
| tests5.cpp:43:2:43:2 | p | semmle.label | p |
| tests5.cpp:55:25:55:38 | call to createLSParser | semmle.label | call to createLSParser |
| tests5.cpp:59:2:59:2 | p | semmle.label | p |
+| tests5.cpp:63:14:63:17 | g_p1 | semmle.label | g_p1 |
+| tests5.cpp:63:21:63:24 | g_p2 | semmle.label | g_p2 |
+| tests5.cpp:67:2:67:32 | Store | semmle.label | Store |
+| tests5.cpp:67:17:67:30 | call to createLSParser | semmle.label | call to createLSParser |
+| tests5.cpp:70:2:70:32 | Store | semmle.label | Store |
+| tests5.cpp:70:17:70:30 | call to createLSParser | semmle.label | call to createLSParser |
+| tests5.cpp:76:2:76:5 | g_p1 | semmle.label | g_p1 |
+| tests5.cpp:77:2:77:5 | g_p2 | semmle.label | g_p2 |
| tests5.cpp:81:25:81:38 | call to createLSParser | semmle.label | call to createLSParser |
| tests5.cpp:83:2:83:2 | p | semmle.label | p |
| tests5.cpp:83:2:83:2 | p | semmle.label | p |
@@ -108,6 +143,9 @@ subpaths
| tests2.cpp:22:2:22:2 | p | tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:20:17:20:31 | SAXParser output argument | XML parser |
| tests2.cpp:37:2:37:2 | p | tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:33:17:33:31 | SAXParser output argument | XML parser |
| tests3.cpp:25:2:25:2 | p | tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:23:21:23:53 | call to createXMLReader | XML parser |
+| tests3.cpp:38:2:38:6 | p_3_3 | tests3.cpp:35:24:35:56 | call to createXMLReader | tests3.cpp:38:2:38:6 | p_3_3 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:35:24:35:56 | call to createXMLReader | XML parser |
+| tests3.cpp:45:2:45:6 | p_3_4 | tests3.cpp:41:24:41:56 | call to createXMLReader | tests3.cpp:45:2:45:6 | p_3_4 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:41:24:41:56 | call to createXMLReader | XML parser |
+| tests3.cpp:56:2:56:6 | p_3_5 | tests3.cpp:48:24:48:56 | call to createXMLReader | tests3.cpp:56:2:56:6 | p_3_5 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:48:24:48:56 | call to createXMLReader | XML parser |
| tests3.cpp:63:2:63:2 | p | tests3.cpp:60:21:60:53 | call to createXMLReader | tests3.cpp:63:2:63:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:60:21:60:53 | call to createXMLReader | XML parser |
| tests3.cpp:70:2:70:2 | p | tests3.cpp:67:21:67:53 | call to createXMLReader | tests3.cpp:70:2:70:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:67:21:67:53 | call to createXMLReader | XML parser |
| tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:26:34:26:48 | (int)... | XML parser |
@@ -118,6 +156,8 @@ subpaths
| tests5.cpp:29:2:29:2 | p | tests5.cpp:27:25:27:38 | call to createLSParser | tests5.cpp:29:2:29:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:27:25:27:38 | call to createLSParser | XML parser |
| tests5.cpp:43:2:43:2 | p | tests5.cpp:40:25:40:38 | call to createLSParser | tests5.cpp:43:2:43:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:40:25:40:38 | call to createLSParser | XML parser |
| tests5.cpp:59:2:59:2 | p | tests5.cpp:55:25:55:38 | call to createLSParser | tests5.cpp:59:2:59:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:55:25:55:38 | call to createLSParser | XML parser |
+| tests5.cpp:76:2:76:5 | g_p1 | tests5.cpp:67:17:67:30 | call to createLSParser | tests5.cpp:76:2:76:5 | g_p1 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:67:17:67:30 | call to createLSParser | XML parser |
+| tests5.cpp:77:2:77:5 | g_p2 | tests5.cpp:70:17:70:30 | call to createLSParser | tests5.cpp:77:2:77:5 | g_p2 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:70:17:70:30 | call to createLSParser | XML parser |
| tests5.cpp:83:2:83:2 | p | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:81:25:81:38 | call to createLSParser | XML parser |
| tests5.cpp:89:2:89:2 | p | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:89:2:89:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:81:25:81:38 | call to createLSParser | XML parser |
| tests.cpp:17:2:17:2 | p | tests.cpp:15:23:15:43 | XercesDOMParser output argument | tests.cpp:17:2:17:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:15:23:15:43 | XercesDOMParser output argument | XML parser |
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp
index 15e518daf13..8af560a8e6d 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp
@@ -35,14 +35,14 @@ void test3_2(InputSource &data) {
SAX2XMLReader *p_3_3 = XMLReaderFactory::createXMLReader();
void test3_3(InputSource &data) {
- p_3_3->parse(data); // BAD (parser not correctly configured) [NOT DETECTED]
+ p_3_3->parse(data); // BAD (parser not correctly configured)
}
SAX2XMLReader *p_3_4 = XMLReaderFactory::createXMLReader();
void test3_4(InputSource &data) {
p_3_4->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);
- p_3_4->parse(data); // GOOD
+ p_3_4->parse(data); // GOOD [FALSE POSITIVE]
}
SAX2XMLReader *p_3_5 = XMLReaderFactory::createXMLReader();
@@ -53,7 +53,7 @@ void test3_5_init() {
void test3_5(InputSource &data) {
test3_5_init();
- p_3_5->parse(data); // GOOD
+ p_3_5->parse(data); // GOOD [FALSE POSITIVE]
}
void test3_6(InputSource &data) {
diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp
index 99027c9bd93..0d55be455da 100644
--- a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp
+++ b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp
@@ -73,8 +73,8 @@ void test5_6_init() {
void test5_6() {
test5_6_init();
- g_p1->parse(*g_data); // GOOD
- g_p2->parse(*g_data); // BAD (parser not correctly configured) [NOT DETECTED]
+ g_p1->parse(*g_data); // GOOD [FALSE POSITIVE]
+ g_p2->parse(*g_data); // BAD (parser not correctly configured)
}
void test5_7(DOMImplementationLS *impl, InputSource &data) {
diff --git a/csharp/codeql-extractor.yml b/csharp/codeql-extractor.yml
index 5d498049a8a..4e1fa7934ce 100644
--- a/csharp/codeql-extractor.yml
+++ b/csharp/codeql-extractor.yml
@@ -4,12 +4,6 @@ version: 1.22.1
column_kind: "utf16"
extra_env_vars:
DOTNET_GENERATE_ASPNET_CERTIFICATE: "false"
- COR_ENABLE_PROFILING: "1"
- COR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}"
- COR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}"
- CORECLR_ENABLE_PROFILING: "1"
- CORECLR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}"
- CORECLR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}"
file_types:
- name: cs
display_name: C# sources
diff --git a/csharp/documentation/library-coverage/coverage.csv b/csharp/documentation/library-coverage/coverage.csv
index 2bc6ca863ac..485242620f3 100644
--- a/csharp/documentation/library-coverage/coverage.csv
+++ b/csharp/documentation/library-coverage/coverage.csv
@@ -1,27 +1,28 @@
-package,sink,source,summary,sink:code,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value
-Dapper,55,,,,,,55,,,,
-JsonToItemsTaskFactory,,,7,,,,,,,7,
-Microsoft.ApplicationBlocks.Data,28,,,,,,28,,,,
-Microsoft.CSharp,,,24,,,,,,,24,
-Microsoft.EntityFrameworkCore,6,,,,,,6,,,,
-Microsoft.Extensions.Caching.Distributed,,,15,,,,,,,15,
-Microsoft.Extensions.Caching.Memory,,,46,,,,,,,45,1
-Microsoft.Extensions.Configuration,,,83,,,,,,,80,3
-Microsoft.Extensions.DependencyInjection,,,62,,,,,,,62,
-Microsoft.Extensions.DependencyModel,,,12,,,,,,,12,
-Microsoft.Extensions.FileProviders,,,15,,,,,,,15,
-Microsoft.Extensions.FileSystemGlobbing,,,15,,,,,,,13,2
-Microsoft.Extensions.Hosting,,,17,,,,,,,16,1
-Microsoft.Extensions.Http,,,10,,,,,,,10,
-Microsoft.Extensions.Logging,,,37,,,,,,,37,
-Microsoft.Extensions.Options,,,8,,,,,,,8,
-Microsoft.Extensions.Primitives,,,63,,,,,,,63,
-Microsoft.Interop,,,27,,,,,,,27,
-Microsoft.NET.Build.Tasks,,,1,,,,,,,1,
-Microsoft.NETCore.Platforms.BuildTasks,,,4,,,,,,,4,
-Microsoft.VisualBasic,,,9,,,,,,,5,4
-Microsoft.Win32,,,8,,,,,,,8,
-MySql.Data.MySqlClient,48,,,,,,48,,,,
-Newtonsoft.Json,,,91,,,,,,,73,18
-ServiceStack,194,,7,27,,75,92,,,7,
-System,28,3,12038,,4,,23,1,3,10096,1942
+package,sink,source,summary,sink:code,sink:encryption-decryptor,sink:encryption-encryptor,sink:encryption-keyprop,sink:encryption-symmetrickey,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value
+Dapper,55,,,,,,,,,,55,,,,
+JsonToItemsTaskFactory,,,7,,,,,,,,,,,7,
+Microsoft.ApplicationBlocks.Data,28,,,,,,,,,,28,,,,
+Microsoft.CSharp,,,24,,,,,,,,,,,24,
+Microsoft.EntityFrameworkCore,6,,,,,,,,,,6,,,,
+Microsoft.Extensions.Caching.Distributed,,,15,,,,,,,,,,,15,
+Microsoft.Extensions.Caching.Memory,,,46,,,,,,,,,,,45,1
+Microsoft.Extensions.Configuration,,,83,,,,,,,,,,,80,3
+Microsoft.Extensions.DependencyInjection,,,62,,,,,,,,,,,62,
+Microsoft.Extensions.DependencyModel,,,12,,,,,,,,,,,12,
+Microsoft.Extensions.FileProviders,,,15,,,,,,,,,,,15,
+Microsoft.Extensions.FileSystemGlobbing,,,15,,,,,,,,,,,13,2
+Microsoft.Extensions.Hosting,,,17,,,,,,,,,,,16,1
+Microsoft.Extensions.Http,,,10,,,,,,,,,,,10,
+Microsoft.Extensions.Logging,,,37,,,,,,,,,,,37,
+Microsoft.Extensions.Options,,,8,,,,,,,,,,,8,
+Microsoft.Extensions.Primitives,,,63,,,,,,,,,,,63,
+Microsoft.Interop,,,27,,,,,,,,,,,27,
+Microsoft.NET.Build.Tasks,,,1,,,,,,,,,,,1,
+Microsoft.NETCore.Platforms.BuildTasks,,,4,,,,,,,,,,,4,
+Microsoft.VisualBasic,,,9,,,,,,,,,,,5,4
+Microsoft.Win32,,,8,,,,,,,,,,,8,
+MySql.Data.MySqlClient,48,,,,,,,,,,48,,,,
+Newtonsoft.Json,,,91,,,,,,,,,,,73,18
+ServiceStack,194,,7,27,,,,,,75,92,,,7,
+System,35,3,11796,,1,1,1,,4,,25,3,3,9854,1942
+Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,,
diff --git a/csharp/documentation/library-coverage/coverage.rst b/csharp/documentation/library-coverage/coverage.rst
index 076d2078d4b..d088c041e8b 100644
--- a/csharp/documentation/library-coverage/coverage.rst
+++ b/csharp/documentation/library-coverage/coverage.rst
@@ -8,7 +8,7 @@ C# framework & library support
Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting`
`ServiceStack `_,"``ServiceStack.*``, ``ServiceStack``",,7,194,
- System,"``System.*``, ``System``",3,12038,28,5
- Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,554,137,
- Totals,,3,12599,359,5
+ System,"``System.*``, ``System``",3,11796,35,7
+ Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``, ``Windows.Security.Cryptography.Core``",,554,138,
+ Totals,,3,12357,367,7
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs
index 8d63c6288bf..ec4f44c21c7 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs
@@ -99,12 +99,6 @@ namespace Semmle.Extraction.CSharp
using var logger = MakeLogger(options.Verbosity, options.Console);
- if (Environment.GetEnvironmentVariable("SEMMLE_CLRTRACER") == "1" && !options.ClrTracer)
- {
- logger.Log(Severity.Info, "Skipping extraction since already extracted from the CLR tracer");
- return ExitCode.Ok;
- }
-
var canonicalPathCache = CanonicalPathCache.Create(logger, 1000);
var pathTransformer = new PathTransformer(canonicalPathCache);
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs
index 7b4e0e95ea3..c2d21d6a16a 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs
@@ -27,11 +27,6 @@ namespace Semmle.Extraction.CSharp
///
public IList CompilerArguments { get; } = new List();
- ///
- /// Holds if the extractor was launched from the CLR tracer.
- ///
- public bool ClrTracer { get; private set; } = false;
-
///
/// Holds if assembly information should be prefixed to TRAP labels.
///
@@ -87,9 +82,6 @@ namespace Semmle.Extraction.CSharp
{
switch (flag)
{
- case "clrtracer":
- ClrTracer = value;
- return true;
case "assemblysensitivetrap":
AssemblySensitiveTrap = value;
return true;
diff --git a/csharp/extractor/Semmle.Extraction.Tests/Options.cs b/csharp/extractor/Semmle.Extraction.Tests/Options.cs
index 9b17320fbaa..2637eb3d5f4 100644
--- a/csharp/extractor/Semmle.Extraction.Tests/Options.cs
+++ b/csharp/extractor/Semmle.Extraction.Tests/Options.cs
@@ -29,7 +29,6 @@ namespace Semmle.Extraction.Tests
Assert.True(options.Threads >= 1);
Assert.Equal(Verbosity.Info, options.Verbosity);
Assert.False(options.Console);
- Assert.False(options.ClrTracer);
Assert.False(options.PDB);
Assert.False(options.Fast);
Assert.Equal(TrapWriter.CompressionMode.Brotli, options.TrapCompression);
diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
index 0bb47844d19..0efa6239b0f 100644
--- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
+++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 1.2.2
+
+## 1.2.1
+
+## 1.2.0
+
## 1.1.4
## 1.1.3
diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.0.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.0.md
new file mode 100644
index 00000000000..0ff42339575
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.0.md
@@ -0,0 +1 @@
+## 1.2.0
diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.1.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.1.md
new file mode 100644
index 00000000000..ddf8866b672
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.1.md
@@ -0,0 +1 @@
+## 1.2.1
diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.2.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.2.md
new file mode 100644
index 00000000000..81af4d86d3b
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.2.2.md
@@ -0,0 +1 @@
+## 1.2.2
diff --git a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
index 26cbcd3f123..0a70a9a01a7 100644
--- a/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
+++ b/csharp/ql/campaigns/Solorigate/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.1.4
+lastReleaseVersion: 1.2.2
diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
index 9cb7bec181f..78cc75ede63 100644
--- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
+++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all
-version: 1.2.0-dev
+version: 1.2.3-dev
groups:
- csharp
- solorigate
diff --git a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
index 0bb47844d19..0efa6239b0f 100644
--- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
+++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
@@ -1,3 +1,9 @@
+## 1.2.2
+
+## 1.2.1
+
+## 1.2.0
+
## 1.1.4
## 1.1.3
diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.0.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.0.md
new file mode 100644
index 00000000000..0ff42339575
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.0.md
@@ -0,0 +1 @@
+## 1.2.0
diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.1.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.1.md
new file mode 100644
index 00000000000..ddf8866b672
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.1.md
@@ -0,0 +1 @@
+## 1.2.1
diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.2.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.2.md
new file mode 100644
index 00000000000..81af4d86d3b
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.2.2.md
@@ -0,0 +1 @@
+## 1.2.2
diff --git a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
index 26cbcd3f123..0a70a9a01a7 100644
--- a/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
+++ b/csharp/ql/campaigns/Solorigate/src/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 1.1.4
+lastReleaseVersion: 1.2.2
diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
index 07419f1b469..fced50b6ef4 100644
--- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml
+++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries
-version: 1.2.0-dev
+version: 1.2.3-dev
groups:
- csharp
- solorigate
diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md
index 3df8b95eeb6..5ea16d73e48 100644
--- a/csharp/ql/lib/CHANGELOG.md
+++ b/csharp/ql/lib/CHANGELOG.md
@@ -1,3 +1,13 @@
+## 0.3.2
+
+## 0.3.1
+
+## 0.3.0
+
+### Deprecated APIs
+
+* The `BarrierGuard` class has been deprecated. Such barriers and sanitizers can now instead be created using the new `BarrierGuard` parameterized module.
+
## 0.2.3
## 0.2.2
diff --git a/csharp/ql/src/IDEContextual.qll b/csharp/ql/lib/IDEContextual.qll
similarity index 100%
rename from csharp/ql/src/IDEContextual.qll
rename to csharp/ql/lib/IDEContextual.qll
diff --git a/java/ql/lib/change-notes/2022-06-21-barrierguard-deprecation.md b/csharp/ql/lib/change-notes/released/0.3.0.md
similarity index 83%
rename from java/ql/lib/change-notes/2022-06-21-barrierguard-deprecation.md
rename to csharp/ql/lib/change-notes/released/0.3.0.md
index 2bd95798f89..54af6e00ac0 100644
--- a/java/ql/lib/change-notes/2022-06-21-barrierguard-deprecation.md
+++ b/csharp/ql/lib/change-notes/released/0.3.0.md
@@ -1,4 +1,5 @@
----
-category: deprecated
----
+## 0.3.0
+
+### Deprecated APIs
+
* The `BarrierGuard` class has been deprecated. Such barriers and sanitizers can now instead be created using the new `BarrierGuard` parameterized module.
diff --git a/csharp/ql/lib/change-notes/released/0.3.1.md b/csharp/ql/lib/change-notes/released/0.3.1.md
new file mode 100644
index 00000000000..2b0719929a1
--- /dev/null
+++ b/csharp/ql/lib/change-notes/released/0.3.1.md
@@ -0,0 +1 @@
+## 0.3.1
diff --git a/csharp/ql/lib/change-notes/released/0.3.2.md b/csharp/ql/lib/change-notes/released/0.3.2.md
new file mode 100644
index 00000000000..8309e697333
--- /dev/null
+++ b/csharp/ql/lib/change-notes/released/0.3.2.md
@@ -0,0 +1 @@
+## 0.3.2
diff --git a/csharp/ql/lib/codeql-pack.release.yml b/csharp/ql/lib/codeql-pack.release.yml
index 0b605901b42..18c64250f42 100644
--- a/csharp/ql/lib/codeql-pack.release.yml
+++ b/csharp/ql/lib/codeql-pack.release.yml
@@ -1,2 +1,2 @@
---
-lastReleaseVersion: 0.2.3
+lastReleaseVersion: 0.3.2
diff --git a/csharp/ql/src/definitions.qll b/csharp/ql/lib/definitions.qll
similarity index 100%
rename from csharp/ql/src/definitions.qll
rename to csharp/ql/lib/definitions.qll
diff --git a/csharp/ql/src/localDefinitions.ql b/csharp/ql/lib/localDefinitions.ql
similarity index 100%
rename from csharp/ql/src/localDefinitions.ql
rename to csharp/ql/lib/localDefinitions.ql
diff --git a/csharp/ql/src/localReferences.ql b/csharp/ql/lib/localReferences.ql
similarity index 100%
rename from csharp/ql/src/localReferences.ql
rename to csharp/ql/lib/localReferences.ql
diff --git a/csharp/ql/src/printAst.ql b/csharp/ql/lib/printAst.ql
similarity index 100%
rename from csharp/ql/src/printAst.ql
rename to csharp/ql/lib/printAst.ql
diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml
index 2e2d17e36fa..8f932e28c7a 100644
--- a/csharp/ql/lib/qlpack.yml
+++ b/csharp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-all
-version: 0.3.0-dev
+version: 0.3.3-dev
groups: csharp
dbscheme: semmlecode.csharp.dbscheme
extractor: csharp
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
index d9bebb9bf17..6bd0d5d033f 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll
@@ -335,7 +335,7 @@ module Expressions {
// ```csharp
// new Dictionary() { [0] = "Zero", [1] = "One", [2] = "Two" }
// ```
- // need special treatment, because the the accesses `[0]`, `[1]`, and `[2]`
+ // need special treatment, because the accesses `[0]`, `[1]`, and `[2]`
// have no qualifier.
this = any(MemberInitializer mi).getLValue()
}
@@ -1288,7 +1288,7 @@ module Statements {
}
final override predicate first(ControlFlowElement first) {
- // Unlike most other statements, `foreach` statements are not modelled in
+ // Unlike most other statements, `foreach` statements are not modeled in
// pre-order, because we use the `foreach` node itself to represent the
// emptiness test that determines whether to execute the loop body
first(this.getIterableExpr(), first)
diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll
index 47fcd24883c..7d0dd10c084 100644
--- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll
+++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll
@@ -881,7 +881,12 @@ import Cached
* graph is restricted to nodes from `RelevantNode`.
*/
module TestOutput {
- abstract class RelevantNode extends Node { }
+ abstract class RelevantNode extends Node {
+ /**
+ * Gets a string used to resolve ties in node and edge ordering.
+ */
+ string getOrderDisambuigation() { result = "" }
+ }
query predicate nodes(RelevantNode n, string attr, string val) {
attr = "semmle.order" and
@@ -894,7 +899,8 @@ module TestOutput {
p
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
- l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString()
+ l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(),
+ p.getOrderDisambuigation()
)
).toString()
}
@@ -916,7 +922,8 @@ module TestOutput {
s
order by
l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(),
- l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString()
+ l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(),
+ s.getOrderDisambuigation()
)
).toString()
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
index 782d33ea020..904ac79f009 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
@@ -377,6 +377,7 @@ module CsvValidation {
exists(string row, string kind | sinkModel(row) |
kind = row.splitAt(";", 7) and
not kind = ["code", "sql", "xss", "remote", "html"] and
+ not kind.matches("encryption-%") and
msg = "Invalid kind \"" + kind + "\" in sink model."
)
or
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
index d35b741fb0c..76c018df03d 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll
@@ -21,7 +21,13 @@ private import semmle.code.csharp.frameworks.system.collections.Generic
*/
DotNet::Callable getCallableForDataFlow(DotNet::Callable c) {
exists(DotNet::Callable unboundDecl | unboundDecl = c.getUnboundDeclaration() |
- result.hasBody() and
+ (
+ result.hasBody()
+ or
+ // take synthesized bodies into account, e.g. implicit constructors
+ // with field initializer assignments
+ result = any(ControlFlow::Nodes::ElementNode n).getEnclosingCallable()
+ ) and
if unboundDecl.getFile().fromSource()
then
// C# callable with C# implementation in the database
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
index 7b9e78d2c4b..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
index 7b9e78d2c4b..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
index 7b9e78d2c4b..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
index 7b9e78d2c4b..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
index 7b9e78d2c4b..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll
index 7b9e78d2c4b..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll
@@ -428,7 +428,7 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- simpleLocalFlowStepExt(n1, n2) and
+ simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config)
)
or
@@ -447,7 +447,7 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config)
)
@@ -466,7 +466,7 @@ private predicate additionalLocalStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -481,7 +481,7 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- jumpStepCached(n1, n2) and
+ jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
)
@@ -494,7 +494,7 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, n2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
@@ -507,7 +507,7 @@ private predicate additionalJumpStateStep(
exists(Node n1, Node n2 |
node1.asNode() = n1 and
node2.asNode() = n2 and
- config.isAdditionalFlowStep(n1, s1, n2, s2) and
+ config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
stepFilter(node1, node2, config) and
not stateBarrier(node1, s1, config) and
@@ -518,7 +518,7 @@ private predicate additionalJumpStateStep(
pragma[nomagic]
private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) {
- readSet(node1.asNode(), c, node2.asNode()) and
+ readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and
stepFilter(node1, node2, config)
or
exists(Node n |
@@ -562,7 +562,8 @@ pragma[nomagic]
private predicate store(
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
) {
- store(node1.asNode(), tc, node2.asNode(), contentType) and
+ store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()),
+ contentType) and
read(_, tc.getContent(), _, config) and
stepFilter(node1, node2, config)
}
@@ -596,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -943,12 +944,9 @@ private module Stage1 {
predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) }
bindingset[node, state, config]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, toReturn, pragma[only_bind_into](config)) and
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, _, pragma[only_bind_into](config)) and
exists(state) and
- exists(returnAp) and
exists(ap)
}
@@ -1141,66 +1139,754 @@ private predicate flowIntoCallNodeCand1(
)
}
-private module Stage2 {
- module PrevStage = Stage1;
+private signature module StageSig {
+ class Ap;
+ predicate revFlow(NodeEx node, Configuration config);
+
+ bindingset[node, state, config]
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ predicate callMayFlowThroughRev(DataFlowCall call, Configuration config);
+
+ predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config);
+
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ );
+
+ predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config);
+}
+
+private module MkStage {
class ApApprox = PrevStage::Ap;
- class Ap = boolean;
+ signature module StageParam {
+ class Ap;
- class ApNil extends Ap {
- ApNil() { this = false }
+ class ApNil extends Ap;
+
+ bindingset[result, ap]
+ ApApprox getApprox(Ap ap);
+
+ ApNil getApNil(NodeEx node);
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail);
+
+ Content getHeadContent(Ap ap);
+
+ class ApOption;
+
+ ApOption apNone();
+
+ ApOption apSome(Ap ap);
+
+ class Cc;
+
+ class CcCall extends Cc;
+
+ // TODO: member predicate on CcCall
+ predicate matchesCall(CcCall cc, DataFlowCall call);
+
+ class CcNoCall extends Cc;
+
+ Cc ccNone();
+
+ CcCall ccSomeCall();
+
+ class LocalCc;
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc);
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc);
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc);
+
+ bindingset[node1, state1, config]
+ bindingset[node2, state2, config]
+ predicate localStep(
+ NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
+ ApNil ap, Configuration config, LocalCc lcc
+ );
+
+ predicate flowOutOfCall(
+ DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
+ );
+
+ predicate flowIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ );
+
+ bindingset[node, state, ap, config]
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config);
+
+ bindingset[ap, contentType]
+ predicate typecheckStore(Ap ap, DataFlowType contentType);
}
- bindingset[result, ap]
- private ApApprox getApprox(Ap ap) { any() }
+ module Stage implements StageSig {
+ import Param
- private ApNil getApNil(NodeEx node) { PrevStage::revFlow(node, _) and exists(result) }
+ /* Begin: Stage logic. */
+ bindingset[result, apa]
+ private ApApprox unbindApa(ApApprox apa) {
+ pragma[only_bind_out](apa) = pragma[only_bind_out](result)
+ }
- bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+ pragma[nomagic]
+ private predicate flowThroughOutOfCall(
+ DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
+ Configuration config
+ ) {
+ flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
+ PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
+ pragma[only_bind_into](config)) and
+ matchesCall(ccc, call)
+ }
- pragma[inline]
- private Content getHeadContent(Ap ap) { exists(result) and ap = true }
+ /**
+ * Holds if `node` is reachable with access path `ap` from a source in the
+ * 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.
+ */
+ pragma[nomagic]
+ predicate fwdFlow(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ fwdFlow0(node, state, cc, argAp, ap, config) and
+ PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and
+ filter(node, state, ap, config)
+ }
- class ApOption = BooleanOption;
+ pragma[nomagic]
+ private predicate fwdFlow0(
+ NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ sourceNode(node, state, config) and
+ (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
+ argAp = apNone() and
+ ap = getApNil(node)
+ or
+ exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
+ fwdFlow(mid, state0, cc, argAp, ap0, config) and
+ localCc = getLocalCc(mid, cc)
+ |
+ localStep(mid, state0, node, state, true, _, config, localCc) and
+ ap = ap0
+ or
+ localStep(mid, state0, node, state, false, ap, config, localCc) and
+ ap0 instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
+ jumpStep(mid, node, config) and
+ cc = ccNone() and
+ argAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
+ additionalJumpStep(mid, node, config) and
+ cc = ccNone() 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
+ additionalJumpStateStep(mid, state0, node, state, config) and
+ cc = ccNone() and
+ argAp = apNone() and
+ ap = getApNil(node)
+ )
+ or
+ // store
+ exists(TypedContent tc, Ap ap0 |
+ fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
+ ap = apCons(tc, ap0)
+ )
+ or
+ // read
+ exists(Ap ap0, Content c |
+ fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
+ fwdFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // flow into a callable
+ exists(ApApprox apa |
+ fwdFlowIn(_, node, state, _, cc, _, ap, config) and
+ apa = getApprox(ap) and
+ if PrevStage::parameterMayFlowThrough(node, _, apa, config)
+ then argAp = apSome(ap)
+ else argAp = apNone()
+ )
+ or
+ // flow out of a callable
+ fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
+ or
+ exists(DataFlowCall call, Ap argAp0 |
+ fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
+ fwdFlowIsEntered(call, cc, argAp, argAp0, config)
+ )
+ }
- ApOption apNone() { result = TBooleanNone() }
+ pragma[nomagic]
+ private predicate fwdFlowStore(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ exists(DataFlowType contentType |
+ fwdFlow(node1, state, cc, argAp, ap1, config) and
+ PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
+ typecheckStore(ap1, contentType)
+ )
+ }
- ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+ /**
+ * Holds if forward flow with access path `tail` reaches a store of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(TypedContent tc |
+ fwdFlowStore(_, tail, tc, _, _, _, _, config) and
+ tc.getContent() = c and
+ cons = apCons(tc, tail)
+ )
+ }
+ pragma[nomagic]
+ private predicate fwdFlowRead(
+ Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
+ Configuration config
+ ) {
+ fwdFlow(node1, state, cc, 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
+ ) {
+ exists(ArgNodeEx arg, boolean allowsFieldFlow |
+ fwdFlow(arg, state, outercc, 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()
+ )
+ }
+
+ pragma[nomagic]
+ private predicate fwdFlowOutNotFromArg(
+ NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(
+ DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
+ DataFlowCallable inner
+ |
+ fwdFlow(ret, state, innercc, argAp, ap, config) and
+ flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
+ inner = ret.getEnclosingCallable() and
+ ccOut = getCallContextReturn(inner, call, innercc) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ 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()
+ )
+ }
+
+ /**
+ * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
+ * and data might flow through the target callable and back out at `call`.
+ */
+ pragma[nomagic]
+ private predicate fwdFlowIsEntered(
+ DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
+ ) {
+ exists(ParamNodeEx p |
+ fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
+ PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate storeStepFwd(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
+ ) {
+ fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
+ ap2 = apCons(tc, ap1) and
+ 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
+ fwdFlowConsCand(ap1, c, ap2, config)
+ }
+
+ pragma[nomagic]
+ private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
+ exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
+ pragma[only_bind_into](config)) and
+ fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
+ fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
+ pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
+ pragma[only_bind_into](config))
+ )
+ }
+
+ pragma[nomagic]
+ private predicate flowThroughIntoCall(
+ DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
+ ) {
+ flowIntoCall(call, arg, p, allowsFieldFlow, config) and
+ fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
+ PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
+ callMayFlowThroughFwd(call, pragma[only_bind_into](config))
+ }
+
+ pragma[nomagic]
+ private predicate returnNodeMayFlowThrough(
+ RetNodeEx ret, FlowState state, Ap ap, Configuration config
+ ) {
+ fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
+ */
+ pragma[nomagic]
+ predicate revFlow(
+ NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
+ ) {
+ revFlow0(node, state, toReturn, 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
+ ) {
+ fwdFlow(node, state, _, _, ap, config) and
+ sinkNode(node, state, config) and
+ (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid |
+ jumpStep(node, mid, config) and
+ revFlow(mid, state, _, _, ap, config) and
+ toReturn = false and
+ returnAp = apNone()
+ )
+ or
+ exists(NodeEx mid, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ exists(NodeEx mid, FlowState state0, ApNil nil |
+ 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
+ returnAp = apNone() and
+ ap instanceof ApNil
+ )
+ or
+ // store
+ exists(Ap ap0, Content c |
+ revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
+ revFlowConsCand(ap0, c, ap, config)
+ )
+ or
+ // read
+ exists(NodeEx mid, Ap ap0 |
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ readStepFwd(node, ap, _, mid, ap0, config)
+ )
+ or
+ // flow into a callable
+ revFlowInNotToReturn(node, state, returnAp, ap, config) and
+ toReturn = false
+ or
+ exists(DataFlowCall call, Ap returnAp0 |
+ revFlowInToReturn(call, node, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, 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()
+ }
+
+ 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
+ ) {
+ revFlow(mid, state, toReturn, returnAp, ap0, config) and
+ storeStepFwd(node, ap, tc, mid, ap0, config) and
+ tc.getContent() = c
+ }
+
+ /**
+ * Holds if reverse flow with access path `tail` reaches a read of `c`
+ * resulting in access path `cons`.
+ */
+ pragma[nomagic]
+ private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
+ exists(NodeEx mid, Ap tail0 |
+ revFlow(mid, _, _, _, tail, config) and
+ tail = pragma[only_bind_into](tail0) and
+ readStepFwd(_, cons, c, mid, tail0, config)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate revFlowOut(
+ DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
+ 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
+ ) {
+ exists(ParamNodeEx p, boolean allowsFieldFlow |
+ revFlow(p, state, true, apSome(returnAp), ap, config) and
+ flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
+ if allowsFieldFlow = false then ap instanceof ApNil else any()
+ )
+ }
+
+ /**
+ * Holds if an output from `call` is reached in the flow covered by `revFlow`
+ * and data might flow through the target callable resulting in reverse flow
+ * reaching an argument of `call`.
+ */
+ pragma[nomagic]
+ private predicate revFlowIsReturned(
+ DataFlowCall call, boolean toReturn, ApOption returnAp, 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
+ matchesCall(ccc, call)
+ )
+ }
+
+ pragma[nomagic]
+ predicate storeStepCand(
+ NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
+ Configuration config
+ ) {
+ exists(Ap ap2, Content c |
+ PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
+ revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
+ revFlowConsCand(ap2, c, ap1, config)
+ )
+ }
+
+ predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
+ exists(Ap ap1, Ap ap2 |
+ revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
+ readStepFwd(node1, ap1, c, node2, ap2, config) and
+ revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
+ pragma[only_bind_into](config))
+ )
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Configuration config) {
+ revFlow(node, state, _, _, _, config)
+ }
+
+ predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, _, _, ap, config)
+ }
+
+ pragma[nomagic]
+ predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
+
+ // use an alias as a workaround for bad functionality-induced joins
+ pragma[nomagic]
+ predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ revFlow(node, state, ap, config)
+ }
+
+ private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepFwd(_, ap, tc, _, _, config)
+ }
+
+ private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
+ storeStepCand(_, ap, tc, _, _, config)
+ }
+
+ private predicate validAp(Ap ap, Configuration config) {
+ revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
+ or
+ exists(TypedContent head, Ap tail |
+ consCand(head, tail, config) and
+ ap = apCons(head, tail)
+ )
+ }
+
+ predicate consCand(TypedContent tc, Ap ap, Configuration config) {
+ revConsCand(tc, ap, config) and
+ validAp(ap, config)
+ }
+
+ pragma[noinline]
+ private predicate parameterFlow(
+ ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
+ ) {
+ revFlow(p, _, true, apSome(ap0), ap, config) and
+ c = p.getEnclosingCallable()
+ }
+
+ 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
+ exists(
+ Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
+ |
+ revFlow(arg, state, toReturn, returnAp, ap, config) and
+ revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
+ revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
+ )
+ }
+
+ predicate stats(
+ 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
+ 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
+ tuples =
+ count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
+ fwdFlow(n, state, cc, argAp, ap, config)
+ )
+ or
+ fwd = false and
+ nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
+ fields = count(TypedContent f0 | consCand(f0, _, config)) and
+ 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)
+ )
+ }
+ /* End: Stage logic. */
+ }
+}
+
+private module BooleanCallContext {
+ class Cc extends boolean {
+ Cc() { this in [true, false] }
+ }
+
+ class CcCall extends Cc {
+ CcCall() { this = true }
+ }
+
+ /** Holds if the call context may be `call`. */
+ predicate matchesCall(CcCall cc, DataFlowCall call) { any() }
+
+ class CcNoCall extends Cc {
+ CcNoCall() { this = false }
+ }
+
+ Cc ccNone() { result = false }
+
+ CcCall ccSomeCall() { result = true }
+
+ class LocalCc = Unit;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
+
+ bindingset[call, c, innercc]
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
+}
+
+private module Level1CallContext {
class Cc = CallContext;
class CcCall = CallContextCall;
+ pragma[inline]
+ predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) }
+
class CcNoCall = CallContextNoCall;
Cc ccNone() { result instanceof CallContextAny }
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
- private class LocalCc = Unit;
+ module NoLocalCallContext {
+ class LocalCc = Unit;
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSiteDispatch(call, c)
- then result = TSpecificCall(call)
- else result = TSomeCall()
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSiteDispatch(call, c)
+ then result = TSpecificCall(call)
+ else result = TSomeCall()
+ }
+ }
+
+ module LocalCallContext {
+ class LocalCc = LocalCallContext;
+
+ bindingset[node, cc]
+ LocalCc getLocalCc(NodeEx node, Cc cc) {
+ result =
+ getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
+ node.getEnclosingCallable())
+ }
+
+ bindingset[call, c, outercc]
+ CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
+ checkCallContextCall(outercc, call, c) and
+ if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
+ }
}
bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
+ CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
checkCallContextReturn(innercc, c, call) and
if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
}
+}
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
+private module Stage2Param implements MkStage::StageParam {
+ private module PrevStage = Stage1;
+
+ class Ap extends boolean {
+ Ap() { this in [true, false] }
+ }
+
+ class ApNil extends Ap {
+ ApNil() { this = false }
+ }
+
+ bindingset[result, ap]
+ PrevStage::Ap getApprox(Ap ap) { any() }
+
+ ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) }
+
+ bindingset[tc, tail]
+ Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) }
+
+ pragma[inline]
+ Content getHeadContent(Ap ap) { exists(result) and ap = true }
+
+ class ApOption = BooleanOption;
+
+ ApOption apNone() { result = TBooleanNone() }
+
+ ApOption apSome(Ap ap) { result = TBooleanSome(ap) }
+
+ import Level1CallContext
+ import NoLocalCallContext
bindingset[node1, state1, config]
bindingset[node2, state2, config]
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -1220,9 +1906,9 @@ private module Stage2 {
exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand1/5;
- private predicate flowIntoCall = flowIntoCallNodeCand1/5;
+ predicate flowIntoCall = flowIntoCallNodeCand1/5;
pragma[nomagic]
private predicate expectsContentCand(NodeEx node, Configuration config) {
@@ -1234,7 +1920,7 @@ private module Stage2 {
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
PrevStage::revFlowState(state, pragma[only_bind_into](config)) and
exists(ap) and
not stateBarrier(node, state, config) and
@@ -1247,542 +1933,11 @@ private module Stage2 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
+}
- /* Begin: Stage 2 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 2 logic. */
+private module Stage2 implements StageSig {
+ import MkStage::Stage
}
pragma[nomagic]
@@ -1882,14 +2037,13 @@ private module LocalFlowBigStep {
) {
additionalLocalFlowStepNodeCand1(node1, node2, config) and
state1 = state2 and
- Stage2::revFlow(node1, pragma[only_bind_into](state1), _, _, false,
- pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), _, _, false,
+ Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false,
pragma[only_bind_into](config))
or
additionalLocalStateStep(node1, state1, node2, state2, config) and
- Stage2::revFlow(node1, state1, _, _, false, pragma[only_bind_into](config)) and
- Stage2::revFlowAlias(node2, state2, _, _, false, pragma[only_bind_into](config))
+ Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and
+ Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config))
}
/**
@@ -1966,26 +2120,24 @@ private module LocalFlowBigStep {
private import LocalFlowBigStep
-private module Stage3 {
- module PrevStage = Stage2;
-
- class ApApprox = PrevStage::Ap;
+private module Stage3Param implements MkStage::StageParam {
+ private module PrevStage = Stage2;
class Ap = AccessPathFront;
class ApNil = AccessPathFrontNil;
- private ApApprox getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathFrontOption;
@@ -1993,44 +2145,18 @@ private module Stage3 {
ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) }
- class Cc = boolean;
+ import BooleanCallContext
- class CcCall extends Cc {
- CcCall() { this = true }
-
- /** Holds if this call context may be `call`. */
- predicate matchesCall(DataFlowCall call) { any() }
- }
-
- class CcNoCall extends Cc {
- CcNoCall() { this = false }
- }
-
- Cc ccNone() { result = false }
-
- CcCall ccSomeCall() { result = true }
-
- private class LocalCc = Unit;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) { any() }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc)
}
- private predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
+ predicate flowOutOfCall = flowOutOfCallNodeCand2/5;
- private predicate flowIntoCall = flowIntoCallNodeCand2/5;
+ predicate flowIntoCall = flowIntoCallNodeCand2/5;
pragma[nomagic]
private predicate clearSet(NodeEx node, ContentSet c, Configuration config) {
@@ -2066,7 +2192,7 @@ private module Stage3 {
private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode }
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) {
exists(state) and
exists(config) and
not clear(node, ap, config) and
@@ -2079,546 +2205,15 @@ private module Stage3 {
}
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) {
+ predicate typecheckStore(Ap ap, DataFlowType contentType) {
// We need to typecheck stores here, since reverse flow through a getter
// might have a different type here compared to inside the getter.
compatibleTypes(ap.getType(), contentType)
}
+}
- /* Begin: Stage 3 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 3 logic. */
+private module Stage3 implements StageSig {
+ import MkStage::Stage
}
/**
@@ -2643,7 +2238,7 @@ private predicate expensiveLen2unfolding(TypedContent tc, Configuration config)
tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and
nodes =
strictcount(NodeEx n, FlowState state |
- Stage3::revFlow(n, state, _, _, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
+ Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
or
flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config)
) and
@@ -2827,26 +2422,24 @@ private class AccessPathApproxOption extends TAccessPathApproxOption {
}
}
-private module Stage4 {
- module PrevStage = Stage3;
-
- class ApApprox = PrevStage::Ap;
+private module Stage4Param implements MkStage::StageParam {
+ private module PrevStage = Stage3;
class Ap = AccessPathApprox;
class ApNil = AccessPathApproxNil;
- private ApApprox getApprox(Ap ap) { result = ap.getFront() }
+ PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() }
- private ApNil getApNil(NodeEx node) {
+ ApNil getApNil(NodeEx node) {
PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType())
}
bindingset[tc, tail]
- private Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
+ Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) }
pragma[noinline]
- private Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
+ Content getHeadContent(Ap ap) { result = ap.getHead().getContent() }
class ApOption = AccessPathApproxOption;
@@ -2854,38 +2447,10 @@ private module Stage4 {
ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) }
- class Cc = CallContext;
+ import Level1CallContext
+ import LocalCallContext
- class CcCall = CallContextCall;
-
- class CcNoCall = CallContextNoCall;
-
- Cc ccNone() { result instanceof CallContextAny }
-
- CcCall ccSomeCall() { result instanceof CallContextSomeCall }
-
- private class LocalCc = LocalCallContext;
-
- bindingset[call, c, outercc]
- private CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) {
- checkCallContextCall(outercc, call, c) and
- if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall()
- }
-
- bindingset[call, c, innercc]
- private CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) {
- checkCallContextReturn(innercc, c, call) and
- if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone()
- }
-
- bindingset[node, cc]
- private LocalCc getLocalCc(NodeEx node, Cc cc) {
- result =
- getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)),
- node.getEnclosingCallable())
- }
-
- private predicate localStep(
+ predicate localStep(
NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue,
ApNil ap, Configuration config, LocalCc lcc
) {
@@ -2893,575 +2458,40 @@ private module Stage4 {
}
pragma[nomagic]
- private predicate flowOutOfCall(
+ predicate flowOutOfCall(
DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config
) {
exists(FlowState state |
flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and
+ PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _,
pragma[only_bind_into](config))
)
}
pragma[nomagic]
- private predicate flowIntoCall(
+ predicate flowIntoCall(
DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow,
Configuration config
) {
exists(FlowState state |
flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and
- PrevStage::revFlow(node2, pragma[only_bind_into](state), _, _, _,
- pragma[only_bind_into](config)) and
- PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, _, _,
+ 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))
)
}
bindingset[node, state, ap, config]
- private predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
+ predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() }
// Type checking is not necessary here as it has already been done in stage 3.
bindingset[ap, contentType]
- private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
-
- /* Begin: Stage 4 logic. */
- bindingset[node, state, config]
- private predicate flowCand(NodeEx node, FlowState state, ApApprox apa, Configuration config) {
- PrevStage::revFlow(node, state, _, _, apa, config)
- }
-
- bindingset[result, apa]
- private ApApprox unbindApa(ApApprox apa) {
- pragma[only_bind_out](apa) = pragma[only_bind_out](result)
- }
-
- pragma[nomagic]
- private predicate flowThroughOutOfCall(
- DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
- Configuration config
- ) {
- flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
- PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
- pragma[only_bind_into](config)) and
- ccc.matchesCall(call)
- }
-
- /**
- * Holds if `node` is reachable with access path `ap` from a source in the
- * 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.
- */
- pragma[nomagic]
- predicate fwdFlow(NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config) {
- fwdFlow0(node, state, cc, argAp, ap, config) and
- flowCand(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
- ) {
- sourceNode(node, state, config) and
- (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
- argAp = apNone() and
- ap = getApNil(node)
- or
- exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc |
- fwdFlow(mid, state0, cc, argAp, ap0, config) and
- localCc = getLocalCc(mid, cc)
- |
- localStep(mid, state0, node, state, true, _, config, localCc) and
- ap = ap0
- or
- localStep(mid, state0, node, state, false, ap, config, localCc) and
- ap0 instanceof ApNil
- )
- or
- exists(NodeEx mid |
- fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and
- jumpStep(mid, node, config) and
- cc = ccNone() and
- argAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and
- additionalJumpStep(mid, node, config) and
- cc = ccNone() 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
- additionalJumpStateStep(mid, state0, node, state, config) and
- cc = ccNone() and
- argAp = apNone() and
- ap = getApNil(node)
- )
- or
- // store
- exists(TypedContent tc, Ap ap0 |
- fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and
- ap = apCons(tc, ap0)
- )
- or
- // read
- exists(Ap ap0, Content c |
- fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and
- fwdFlowConsCand(ap0, c, ap, config)
- )
- or
- // flow into a callable
- exists(ApApprox apa |
- fwdFlowIn(_, node, state, _, cc, _, ap, config) and
- apa = getApprox(ap) and
- if PrevStage::parameterMayFlowThrough(node, _, apa, config)
- then argAp = apSome(ap)
- else argAp = apNone()
- )
- or
- // flow out of a callable
- fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config)
- or
- exists(DataFlowCall call, Ap argAp0 |
- fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and
- fwdFlowIsEntered(call, cc, argAp, argAp0, config)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowStore(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- exists(DataFlowType contentType |
- fwdFlow(node1, state, cc, argAp, ap1, config) and
- PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
- typecheckStore(ap1, contentType)
- )
- }
-
- /**
- * Holds if forward flow with access path `tail` reaches a store of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(TypedContent tc |
- fwdFlowStore(_, tail, tc, _, _, _, _, config) and
- tc.getContent() = c and
- cons = apCons(tc, tail)
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowRead(
- Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp,
- Configuration config
- ) {
- fwdFlow(node1, state, cc, 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
- ) {
- exists(ArgNodeEx arg, boolean allowsFieldFlow |
- fwdFlow(arg, state, outercc, 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()
- )
- }
-
- pragma[nomagic]
- private predicate fwdFlowOutNotFromArg(
- NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(
- DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc,
- DataFlowCallable inner
- |
- fwdFlow(ret, state, innercc, argAp, ap, config) and
- flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
- inner = ret.getEnclosingCallable() and
- ccOut = getCallContextReturn(inner, call, innercc) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- 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()
- )
- }
-
- /**
- * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`
- * and data might flow through the target callable and back out at `call`.
- */
- pragma[nomagic]
- private predicate fwdFlowIsEntered(
- DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config
- ) {
- exists(ParamNodeEx p |
- fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and
- PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
- )
- }
-
- pragma[nomagic]
- private predicate storeStepFwd(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config
- ) {
- fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and
- ap2 = apCons(tc, ap1) and
- 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
- fwdFlowConsCand(ap1, c, ap2, config)
- }
-
- pragma[nomagic]
- private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) {
- exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap,
- pragma[only_bind_into](config)) and
- fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and
- fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc),
- pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0),
- pragma[only_bind_into](config))
- )
- }
-
- pragma[nomagic]
- private predicate flowThroughIntoCall(
- DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config
- ) {
- flowIntoCall(call, arg, p, allowsFieldFlow, config) and
- fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and
- PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and
- callMayFlowThroughFwd(call, pragma[only_bind_into](config))
- }
-
- pragma[nomagic]
- private predicate returnNodeMayFlowThrough(
- RetNodeEx ret, FlowState state, Ap ap, Configuration config
- ) {
- fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, 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.
- */
- pragma[nomagic]
- predicate revFlow(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow0(node, state, toReturn, 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
- ) {
- fwdFlow(node, state, _, _, ap, config) and
- sinkNode(node, state, config) and
- (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) 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)
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid |
- jumpStep(node, mid, config) and
- revFlow(mid, state, _, _, ap, config) and
- toReturn = false and
- returnAp = apNone()
- )
- or
- exists(NodeEx mid, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- exists(NodeEx mid, FlowState state0, ApNil nil |
- 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
- returnAp = apNone() and
- ap instanceof ApNil
- )
- or
- // store
- exists(Ap ap0, Content c |
- revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and
- revFlowConsCand(ap0, c, ap, config)
- )
- or
- // read
- exists(NodeEx mid, Ap ap0 |
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- readStepFwd(node, ap, _, mid, ap0, config)
- )
- or
- // flow into a callable
- revFlowInNotToReturn(node, state, returnAp, ap, config) and
- toReturn = false
- or
- exists(DataFlowCall call, Ap returnAp0 |
- revFlowInToReturn(call, node, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, 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()
- }
-
- 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
- ) {
- revFlow(mid, state, toReturn, returnAp, ap0, config) and
- storeStepFwd(node, ap, tc, mid, ap0, config) and
- tc.getContent() = c
- }
-
- /**
- * Holds if reverse flow with access path `tail` reaches a read of `c`
- * resulting in access path `cons`.
- */
- pragma[nomagic]
- private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) {
- exists(NodeEx mid, Ap tail0 |
- revFlow(mid, _, _, _, tail, config) and
- tail = pragma[only_bind_into](tail0) and
- readStepFwd(_, cons, c, mid, tail0, config)
- )
- }
-
- pragma[nomagic]
- private predicate revFlowOut(
- DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, 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
- 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
- ) {
- exists(ParamNodeEx p, boolean allowsFieldFlow |
- revFlow(p, state, true, apSome(returnAp), ap, config) and
- flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
- if allowsFieldFlow = false then ap instanceof ApNil else any()
- )
- }
-
- /**
- * Holds if an output from `call` is reached in the flow covered by `revFlow`
- * and data might flow through the target callable resulting in reverse flow
- * reaching an argument of `call`.
- */
- pragma[nomagic]
- private predicate revFlowIsReturned(
- DataFlowCall call, boolean toReturn, ApOption returnAp, 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
- ccc.matchesCall(call)
- )
- }
-
- pragma[nomagic]
- predicate storeStepCand(
- NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType,
- Configuration config
- ) {
- exists(Ap ap2, Content c |
- PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and
- revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and
- revFlowConsCand(ap2, c, ap1, config)
- )
- }
-
- predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) {
- exists(Ap ap1, Ap ap2 |
- revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and
- readStepFwd(node1, ap1, c, node2, ap2, config) and
- revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _,
- pragma[only_bind_into](config))
- )
- }
-
- predicate revFlow(NodeEx node, FlowState state, Configuration config) {
- revFlow(node, state, _, _, _, config)
- }
-
- pragma[nomagic]
- predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) }
-
- // use an alias as a workaround for bad functionality-induced joins
- pragma[nomagic]
- predicate revFlowAlias(
- NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config
- ) {
- revFlow(node, state, toReturn, returnAp, ap, config)
- }
-
- private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepFwd(_, ap, tc, _, _, config)
- }
-
- private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) {
- storeStepCand(_, ap, tc, _, _, config)
- }
-
- private predicate validAp(Ap ap, Configuration config) {
- revFlow(_, _, _, _, ap, config) and ap instanceof ApNil
- or
- exists(TypedContent head, Ap tail |
- consCand(head, tail, config) and
- ap = apCons(head, tail)
- )
- }
-
- predicate consCand(TypedContent tc, Ap ap, Configuration config) {
- revConsCand(tc, ap, config) and
- validAp(ap, config)
- }
-
- pragma[noinline]
- private predicate parameterFlow(
- ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config
- ) {
- revFlow(p, _, true, apSome(ap0), ap, config) and
- c = p.getEnclosingCallable()
- }
-
- 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 callMayFlowThroughRev(DataFlowCall call, Configuration config) {
- exists(
- Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap
- |
- revFlow(arg, state, toReturn, returnAp, ap, config) and
- revFlowInToReturn(call, arg, state, returnAp0, ap, config) and
- revFlowIsReturned(call, toReturn, returnAp, returnAp0, config)
- )
- }
-
- predicate stats(
- 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
- 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
- tuples =
- count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap |
- fwdFlow(n, state, cc, argAp, ap, config)
- )
- or
- fwd = false and
- nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and
- fields = count(TypedContent f0 | consCand(f0, _, config)) and
- 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)
- )
- }
- /* End: Stage 4 logic. */
+ predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
}
+private module Stage4 = MkStage::Stage;
+
bindingset[conf, result]
private Configuration unbindConf(Configuration conf) {
exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c))
@@ -3494,7 +2524,7 @@ private newtype TSummaryCtx =
TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) {
exists(Configuration config |
Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and
- Stage4::revFlow(p, state, _, _, _, config)
+ Stage4::revFlow(p, state, _, config)
)
}
@@ -3552,7 +2582,7 @@ private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) {
private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) {
result =
strictcount(NodeEx n, FlowState state |
- Stage4::revFlow(n, state, _, _, apa, config) or nodeMayUseSummary(n, state, apa, config)
+ Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config)
)
}
@@ -3666,7 +2696,7 @@ private newtype TPathNode =
exists(PathNodeMid mid |
pathStep(mid, node, state, cc, sc, ap) and
pragma[only_bind_into](config) = mid.getConfiguration() and
- Stage4::revFlow(node, state, _, _, ap.getApprox(), pragma[only_bind_into](config))
+ Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config))
)
} or
TPathNodeSink(NodeEx node, FlowState state, Configuration config) {
@@ -4206,7 +3236,7 @@ private NodeEx getAnOutNodeFlow(
ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config
) {
result.asNode() = kind.getAnOutNode(call) and
- Stage4::revFlow(result, _, _, _, apa, config)
+ Stage4::revFlow(result, _, apa, config)
}
/**
@@ -4242,7 +3272,7 @@ private predicate parameterCand(
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
) {
exists(ParamNodeEx p |
- Stage4::revFlow(p, _, _, _, apa, config) and
+ Stage4::revFlow(p, _, apa, config) and
p.isParameterOf(callable, pos)
)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
index f263ee0114b..ec29d704248 100755
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
@@ -149,7 +149,7 @@ private module Cached {
// Taint members
readStep(nodeFrom, any(TaintedMember m).(FieldOrProperty).getContent(), nodeTo)
or
- // Although flow through collections is modelled precisely using stores/reads, we still
+ // Although flow through collections is modeled precisely using stores/reads, we still
// allow flow out of a _tainted_ collection. This is needed in order to support taint-
// tracking configurations where the source is a collection
readStep(nodeFrom, TElementContent(), nodeTo)
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll
index 221c0f0fac6..5fb1c2f1aa5 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll
@@ -255,10 +255,10 @@ module JsonNET {
[
"Newtonsoft.Json.Linq;JObject;false;Add;(System.Collections.Generic.KeyValuePair);;Argument[0].Property[System.Collections.Generic.KeyValuePair<,>.Key];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual",
"Newtonsoft.Json.Linq;JObject;false;Add;(System.Collections.Generic.KeyValuePair);;Argument[0].Property[System.Collections.Generic.KeyValuePair<,>.Value];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual",
- "Newtonsoft.Json.Linq;JObject;false;JObject;(Newtonsoft.Json.Linq.JObject);;Argument[0].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];ReturnValue.Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual",
- "Newtonsoft.Json.Linq;JObject;false;JObject;(Newtonsoft.Json.Linq.JObject);;Argument[0].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual",
- "Newtonsoft.Json.Linq;JObject;false;JObject;(System.Object[]);;Argument[0].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];ReturnValue.Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual",
- "Newtonsoft.Json.Linq;JObject;false;JObject;(System.Object[]);;Argument[0].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual",
+ "Newtonsoft.Json.Linq;JObject;false;JObject;(Newtonsoft.Json.Linq.JObject);;Argument[0].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual",
+ "Newtonsoft.Json.Linq;JObject;false;JObject;(Newtonsoft.Json.Linq.JObject);;Argument[0].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual",
+ "Newtonsoft.Json.Linq;JObject;false;JObject;(System.Object[]);;Argument[0].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual",
+ "Newtonsoft.Json.Linq;JObject;false;JObject;(System.Object[]);;Argument[0].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual",
"Newtonsoft.Json.Linq;JObject;false;Parse;(System.String);;Argument[0];ReturnValue;taint;manual",
"Newtonsoft.Json.Linq;JObject;false;Parse;(System.String,Newtonsoft.Json.Linq.JsonLoadSettings);;Argument[0];ReturnValue;taint;manual",
"Newtonsoft.Json.Linq;JObject;false;get_Item;(System.Object);;Argument[Qualifier].Element;ReturnValue;value;manual",
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/ServiceStack.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/ServiceStack.qll
index 1145933352f..2f54ae3e08b 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/ServiceStack.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/ServiceStack.qll
@@ -282,13 +282,13 @@ private class ServiceStackXssSummaryModelCsv extends SummaryModelCsv {
override predicate row(string row) {
row =
[
- "ServiceStack;HttpResult;false;HttpResult;(System.String,System.String);;Argument[0];ReturnValue;taint;manual",
- "ServiceStack;HttpResult;false;HttpResult;(System.Object,System.String,System.Net.HttpStatusCode);;Argument[0];ReturnValue;taint;manual",
- "ServiceStack;HttpResult;false;HttpResult;(System.Object,System.String);;Argument[0];ReturnValue;taint;manual",
- "ServiceStack;HttpResult;false;HttpResult;(System.Object,System.Net.HttpStatusCode);;Argument[0];ReturnValue;taint;manual",
- "ServiceStack;HttpResult;false;HttpResult;(System.Object);;Argument[0];ReturnValue;taint;manual",
- "ServiceStack;HttpResult;false;HttpResult;(System.IO.Stream,System.String);;Argument[0];ReturnValue;taint;manual",
- "ServiceStack;HttpResult;false;HttpResult;(System.Byte[],System.String);;Argument[0];ReturnValue;taint;manual"
+ "ServiceStack;HttpResult;false;HttpResult;(System.String,System.String);;Argument[0];Argument[Qualifier];taint;manual",
+ "ServiceStack;HttpResult;false;HttpResult;(System.Object,System.String,System.Net.HttpStatusCode);;Argument[0];Argument[Qualifier];taint;manual",
+ "ServiceStack;HttpResult;false;HttpResult;(System.Object,System.String);;Argument[0];Argument[Qualifier];taint;manual",
+ "ServiceStack;HttpResult;false;HttpResult;(System.Object,System.Net.HttpStatusCode);;Argument[0];Argument[Qualifier];taint;manual",
+ "ServiceStack;HttpResult;false;HttpResult;(System.Object);;Argument[0];Argument[Qualifier];taint;manual",
+ "ServiceStack;HttpResult;false;HttpResult;(System.IO.Stream,System.String);;Argument[0];Argument[Qualifier];taint;manual",
+ "ServiceStack;HttpResult;false;HttpResult;(System.Byte[],System.String);;Argument[0];Argument[Qualifier];taint;manual"
]
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll
index b4eb0b00fc5..6621c6a7835 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/System.qll
@@ -622,9 +622,9 @@ private class SystemLazyFlowModelCsv extends SummaryModelCsv {
override predicate row(string row) {
row =
[
- "System;Lazy<>;false;Lazy;(System.Func);;Argument[0].ReturnValue;ReturnValue.Property[System.Lazy<>.Value];value;manual",
- "System;Lazy<>;false;Lazy;(System.Func,System.Boolean);;Argument[0].ReturnValue;ReturnValue.Property[System.Lazy<>.Value];value;manual",
- "System;Lazy<>;false;Lazy;(System.Func,System.Threading.LazyThreadSafetyMode);;Argument[0].ReturnValue;ReturnValue.Property[System.Lazy<>.Value];value;manual",
+ "System;Lazy<>;false;Lazy;(System.Func);;Argument[0].ReturnValue;Argument[Qualifier].Property[System.Lazy<>.Value];value;manual",
+ "System;Lazy<>;false;Lazy;(System.Func,System.Boolean);;Argument[0].ReturnValue;Argument[Qualifier].Property[System.Lazy<>.Value];value;manual",
+ "System;Lazy<>;false;Lazy;(System.Func,System.Threading.LazyThreadSafetyMode);;Argument[0].ReturnValue;Argument[Qualifier].Property[System.Lazy<>.Value];value;manual",
"System;Lazy<>;false;get_Value;();;Argument[Qualifier];ReturnValue;taint;manual",
]
}
@@ -667,7 +667,7 @@ private class SystemNullableFlowModelCsv extends SummaryModelCsv {
"System;Nullable<>;false;GetValueOrDefault;();;Argument[Qualifier].Property[System.Nullable<>.Value];ReturnValue;value;manual",
"System;Nullable<>;false;GetValueOrDefault;(T);;Argument[0];ReturnValue;value;manual",
"System;Nullable<>;false;GetValueOrDefault;(T);;Argument[Qualifier].Property[System.Nullable<>.Value];ReturnValue;value;manual",
- "System;Nullable<>;false;Nullable;(T);;Argument[0];ReturnValue.Property[System.Nullable<>.Value];value;manual",
+ "System;Nullable<>;false;Nullable;(T);;Argument[0];Argument[Qualifier].Property[System.Nullable<>.Value];value;manual",
"System;Nullable<>;false;get_HasValue;();;Argument[Qualifier].Property[System.Nullable<>.Value];ReturnValue;taint;manual",
"System;Nullable<>;false;get_Value;();;Argument[Qualifier];ReturnValue;taint;manual",
]
@@ -981,8 +981,8 @@ private class SystemStringFlowModelCsv extends SummaryModelCsv {
"System;String;false;Split;(System.String,System.StringSplitOptions);;Argument[Qualifier];ReturnValue.Element;taint;manual",
"System;String;false;Split;(System.String[],System.Int32,System.StringSplitOptions);;Argument[Qualifier];ReturnValue.Element;taint;manual",
"System;String;false;Split;(System.String[],System.StringSplitOptions);;Argument[Qualifier];ReturnValue.Element;taint;manual",
- "System;String;false;String;(System.Char[]);;Argument[0].Element;ReturnValue;taint;manual",
- "System;String;false;String;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;manual",
+ "System;String;false;String;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;manual",
+ "System;String;false;String;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual",
"System;String;false;Substring;(System.Int32);;Argument[Qualifier];ReturnValue;taint;manual",
"System;String;false;Substring;(System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;manual",
"System;String;false;ToLower;();;Argument[Qualifier];ReturnValue;taint;manual",
@@ -1073,9 +1073,9 @@ private class SystemUriFlowModelCsv extends SummaryModelCsv {
row =
[
"System;Uri;false;ToString;();;Argument[Qualifier];ReturnValue;taint;manual",
- "System;Uri;false;Uri;(System.String);;Argument[0];ReturnValue;taint;manual",
- "System;Uri;false;Uri;(System.String,System.Boolean);;Argument[0];ReturnValue;taint;manual",
- "System;Uri;false;Uri;(System.String,System.UriKind);;Argument[0];ReturnValue;taint;manual",
+ "System;Uri;false;Uri;(System.String);;Argument[0];Argument[Qualifier];taint;manual",
+ "System;Uri;false;Uri;(System.String,System.Boolean);;Argument[0];Argument[Qualifier];taint;manual",
+ "System;Uri;false;Uri;(System.String,System.UriKind);;Argument[0];Argument[Qualifier];taint;manual",
"System;Uri;false;get_OriginalString;();;Argument[Qualifier];ReturnValue;taint;manual",
"System;Uri;false;get_PathAndQuery;();;Argument[Qualifier];ReturnValue;taint;manual",
"System;Uri;false;get_Query;();;Argument[Qualifier];ReturnValue;taint;manual",
@@ -1364,13 +1364,13 @@ private class SystemTupleTFlowModelCsv extends SummaryModelCsv {
override predicate row(string row) {
row =
[
- "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[0];ReturnValue.Property[System.Tuple<,,,,,,,>.Item1];value;manual",
- "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[1];ReturnValue.Property[System.Tuple<,,,,,,,>.Item2];value;manual",
- "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[2];ReturnValue.Property[System.Tuple<,,,,,,,>.Item3];value;manual",
- "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[3];ReturnValue.Property[System.Tuple<,,,,,,,>.Item4];value;manual",
- "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[4];ReturnValue.Property[System.Tuple<,,,,,,,>.Item5];value;manual",
- "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[5];ReturnValue.Property[System.Tuple<,,,,,,,>.Item6];value;manual",
- "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[6];ReturnValue.Property[System.Tuple<,,,,,,,>.Item7];value;manual",
+ "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[0];Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item1];value;manual",
+ "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[1];Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item2];value;manual",
+ "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[2];Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item3];value;manual",
+ "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[3];Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item4];value;manual",
+ "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[4];Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item5];value;manual",
+ "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[5];Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item6];value;manual",
+ "System;Tuple<,,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[6];Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item7];value;manual",
"System;Tuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item1];ReturnValue;value;manual",
"System;Tuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item2];ReturnValue;value;manual",
"System;Tuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item3];ReturnValue;value;manual",
@@ -1378,13 +1378,13 @@ private class SystemTupleTFlowModelCsv extends SummaryModelCsv {
"System;Tuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item5];ReturnValue;value;manual",
"System;Tuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item6];ReturnValue;value;manual",
"System;Tuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,,>.Item7];ReturnValue;value;manual",
- "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[0];ReturnValue.Property[System.Tuple<,,,,,,>.Item1];value;manual",
- "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[1];ReturnValue.Property[System.Tuple<,,,,,,>.Item2];value;manual",
- "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[2];ReturnValue.Property[System.Tuple<,,,,,,>.Item3];value;manual",
- "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[3];ReturnValue.Property[System.Tuple<,,,,,,>.Item4];value;manual",
- "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[4];ReturnValue.Property[System.Tuple<,,,,,,>.Item5];value;manual",
- "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[5];ReturnValue.Property[System.Tuple<,,,,,,>.Item6];value;manual",
- "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[6];ReturnValue.Property[System.Tuple<,,,,,,>.Item7];value;manual",
+ "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[0];Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item1];value;manual",
+ "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[1];Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item2];value;manual",
+ "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[2];Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item3];value;manual",
+ "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[3];Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item4];value;manual",
+ "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[4];Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item5];value;manual",
+ "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[5];Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item6];value;manual",
+ "System;Tuple<,,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[6];Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item7];value;manual",
"System;Tuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item1];ReturnValue;value;manual",
"System;Tuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item2];ReturnValue;value;manual",
"System;Tuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item3];ReturnValue;value;manual",
@@ -1392,47 +1392,47 @@ private class SystemTupleTFlowModelCsv extends SummaryModelCsv {
"System;Tuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item5];ReturnValue;value;manual",
"System;Tuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item6];ReturnValue;value;manual",
"System;Tuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,,>.Item7];ReturnValue;value;manual",
- "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[0];ReturnValue.Property[System.Tuple<,,,,,>.Item1];value;manual",
- "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[1];ReturnValue.Property[System.Tuple<,,,,,>.Item2];value;manual",
- "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[2];ReturnValue.Property[System.Tuple<,,,,,>.Item3];value;manual",
- "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[3];ReturnValue.Property[System.Tuple<,,,,,>.Item4];value;manual",
- "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[4];ReturnValue.Property[System.Tuple<,,,,,>.Item5];value;manual",
- "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[5];ReturnValue.Property[System.Tuple<,,,,,>.Item6];value;manual",
+ "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[0];Argument[Qualifier].Property[System.Tuple<,,,,,>.Item1];value;manual",
+ "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[1];Argument[Qualifier].Property[System.Tuple<,,,,,>.Item2];value;manual",
+ "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[2];Argument[Qualifier].Property[System.Tuple<,,,,,>.Item3];value;manual",
+ "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[3];Argument[Qualifier].Property[System.Tuple<,,,,,>.Item4];value;manual",
+ "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[4];Argument[Qualifier].Property[System.Tuple<,,,,,>.Item5];value;manual",
+ "System;Tuple<,,,,,>;false;Tuple;(T1,T2,T3,T4,T5,T6);;Argument[5];Argument[Qualifier].Property[System.Tuple<,,,,,>.Item6];value;manual",
"System;Tuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,>.Item1];ReturnValue;value;manual",
"System;Tuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,>.Item2];ReturnValue;value;manual",
"System;Tuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,>.Item3];ReturnValue;value;manual",
"System;Tuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,>.Item4];ReturnValue;value;manual",
"System;Tuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,>.Item5];ReturnValue;value;manual",
"System;Tuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,,>.Item6];ReturnValue;value;manual",
- "System;Tuple<,,,,>;false;Tuple;(T1,T2,T3,T4,T5);;Argument[0];ReturnValue.Property[System.Tuple<,,,,>.Item1];value;manual",
- "System;Tuple<,,,,>;false;Tuple;(T1,T2,T3,T4,T5);;Argument[1];ReturnValue.Property[System.Tuple<,,,,>.Item2];value;manual",
- "System;Tuple<,,,,>;false;Tuple;(T1,T2,T3,T4,T5);;Argument[2];ReturnValue.Property[System.Tuple<,,,,>.Item3];value;manual",
- "System;Tuple<,,,,>;false;Tuple;(T1,T2,T3,T4,T5);;Argument[3];ReturnValue.Property[System.Tuple<,,,,>.Item4];value;manual",
- "System;Tuple<,,,,>;false;Tuple;(T1,T2,T3,T4,T5);;Argument[4];ReturnValue.Property[System.Tuple<,,,,>.Item5];value;manual",
+ "System;Tuple<,,,,>;false;Tuple;(T1,T2,T3,T4,T5);;Argument[0];Argument[Qualifier].Property[System.Tuple<,,,,>.Item1];value;manual",
+ "System;Tuple<,,,,>;false;Tuple;(T1,T2,T3,T4,T5);;Argument[1];Argument[Qualifier].Property[System.Tuple<,,,,>.Item2];value;manual",
+ "System;Tuple<,,,,>;false;Tuple;(T1,T2,T3,T4,T5);;Argument[2];Argument[Qualifier].Property[System.Tuple<,,,,>.Item3];value;manual",
+ "System;Tuple<,,,,>;false;Tuple;(T1,T2,T3,T4,T5);;Argument[3];Argument[Qualifier].Property[System.Tuple<,,,,>.Item4];value;manual",
+ "System;Tuple<,,,,>;false;Tuple;(T1,T2,T3,T4,T5);;Argument[4];Argument[Qualifier].Property[System.Tuple<,,,,>.Item5];value;manual",
"System;Tuple<,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,>.Item1];ReturnValue;value;manual",
"System;Tuple<,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,>.Item2];ReturnValue;value;manual",
"System;Tuple<,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,>.Item3];ReturnValue;value;manual",
"System;Tuple<,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,>.Item4];ReturnValue;value;manual",
"System;Tuple<,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,,>.Item5];ReturnValue;value;manual",
- "System;Tuple<,,,>;false;Tuple;(T1,T2,T3,T4);;Argument[0];ReturnValue.Property[System.Tuple<,,,>.Item1];value;manual",
- "System;Tuple<,,,>;false;Tuple;(T1,T2,T3,T4);;Argument[1];ReturnValue.Property[System.Tuple<,,,>.Item2];value;manual",
- "System;Tuple<,,,>;false;Tuple;(T1,T2,T3,T4);;Argument[2];ReturnValue.Property[System.Tuple<,,,>.Item3];value;manual",
- "System;Tuple<,,,>;false;Tuple;(T1,T2,T3,T4);;Argument[3];ReturnValue.Property[System.Tuple<,,,>.Item4];value;manual",
+ "System;Tuple<,,,>;false;Tuple;(T1,T2,T3,T4);;Argument[0];Argument[Qualifier].Property[System.Tuple<,,,>.Item1];value;manual",
+ "System;Tuple<,,,>;false;Tuple;(T1,T2,T3,T4);;Argument[1];Argument[Qualifier].Property[System.Tuple<,,,>.Item2];value;manual",
+ "System;Tuple<,,,>;false;Tuple;(T1,T2,T3,T4);;Argument[2];Argument[Qualifier].Property[System.Tuple<,,,>.Item3];value;manual",
+ "System;Tuple<,,,>;false;Tuple;(T1,T2,T3,T4);;Argument[3];Argument[Qualifier].Property[System.Tuple<,,,>.Item4];value;manual",
"System;Tuple<,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,>.Item1];ReturnValue;value;manual",
"System;Tuple<,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,>.Item2];ReturnValue;value;manual",
"System;Tuple<,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,>.Item3];ReturnValue;value;manual",
"System;Tuple<,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,,>.Item4];ReturnValue;value;manual",
- "System;Tuple<,,>;false;Tuple;(T1,T2,T3);;Argument[0];ReturnValue.Property[System.Tuple<,,>.Item1];value;manual",
- "System;Tuple<,,>;false;Tuple;(T1,T2,T3);;Argument[1];ReturnValue.Property[System.Tuple<,,>.Item2];value;manual",
- "System;Tuple<,,>;false;Tuple;(T1,T2,T3);;Argument[2];ReturnValue.Property[System.Tuple<,,>.Item3];value;manual",
+ "System;Tuple<,,>;false;Tuple;(T1,T2,T3);;Argument[0];Argument[Qualifier].Property[System.Tuple<,,>.Item1];value;manual",
+ "System;Tuple<,,>;false;Tuple;(T1,T2,T3);;Argument[1];Argument[Qualifier].Property[System.Tuple<,,>.Item2];value;manual",
+ "System;Tuple<,,>;false;Tuple;(T1,T2,T3);;Argument[2];Argument[Qualifier].Property[System.Tuple<,,>.Item3];value;manual",
"System;Tuple<,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,>.Item1];ReturnValue;value;manual",
"System;Tuple<,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,>.Item2];ReturnValue;value;manual",
"System;Tuple<,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,,>.Item3];ReturnValue;value;manual",
- "System;Tuple<,>;false;Tuple;(T1,T2);;Argument[0];ReturnValue.Property[System.Tuple<,>.Item1];value;manual",
- "System;Tuple<,>;false;Tuple;(T1,T2);;Argument[1];ReturnValue.Property[System.Tuple<,>.Item2];value;manual",
+ "System;Tuple<,>;false;Tuple;(T1,T2);;Argument[0];Argument[Qualifier].Property[System.Tuple<,>.Item1];value;manual",
+ "System;Tuple<,>;false;Tuple;(T1,T2);;Argument[1];Argument[Qualifier].Property[System.Tuple<,>.Item2];value;manual",
"System;Tuple<,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,>.Item1];ReturnValue;value;manual",
"System;Tuple<,>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<,>.Item2];ReturnValue;value;manual",
- "System;Tuple<>;false;Tuple;(T1);;Argument[0];ReturnValue.Property[System.Tuple<>.Item1];value;manual",
+ "System;Tuple<>;false;Tuple;(T1);;Argument[0];Argument[Qualifier].Property[System.Tuple<>.Item1];value;manual",
"System;Tuple<>;false;get_Item;(System.Int32);;Argument[Qualifier].Property[System.Tuple<>.Item1];ReturnValue;value;manual",
]
}
@@ -1622,13 +1622,13 @@ private class SystemValueTupleTFlowModelCsv extends SummaryModelCsv {
override predicate row(string row) {
row =
[
- "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[0];ReturnValue.Field[System.ValueTuple<,,,,,,,>.Item1];value;manual",
- "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[1];ReturnValue.Field[System.ValueTuple<,,,,,,,>.Item2];value;manual",
- "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[2];ReturnValue.Field[System.ValueTuple<,,,,,,,>.Item3];value;manual",
- "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[3];ReturnValue.Field[System.ValueTuple<,,,,,,,>.Item4];value;manual",
- "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[4];ReturnValue.Field[System.ValueTuple<,,,,,,,>.Item5];value;manual",
- "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[5];ReturnValue.Field[System.ValueTuple<,,,,,,,>.Item6];value;manual",
- "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[6];ReturnValue.Field[System.ValueTuple<,,,,,,,>.Item7];value;manual",
+ "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[0];Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item1];value;manual",
+ "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[1];Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item2];value;manual",
+ "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[2];Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item3];value;manual",
+ "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[3];Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item4];value;manual",
+ "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[4];Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item5];value;manual",
+ "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[5];Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item6];value;manual",
+ "System;ValueTuple<,,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7,TRest);;Argument[6];Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item7];value;manual",
"System;ValueTuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item1];ReturnValue;value;manual",
"System;ValueTuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item2];ReturnValue;value;manual",
"System;ValueTuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item3];ReturnValue;value;manual",
@@ -1636,13 +1636,13 @@ private class SystemValueTupleTFlowModelCsv extends SummaryModelCsv {
"System;ValueTuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item5];ReturnValue;value;manual",
"System;ValueTuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item6];ReturnValue;value;manual",
"System;ValueTuple<,,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,,>.Item7];ReturnValue;value;manual",
- "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[0];ReturnValue.Field[System.ValueTuple<,,,,,,>.Item1];value;manual",
- "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[1];ReturnValue.Field[System.ValueTuple<,,,,,,>.Item2];value;manual",
- "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[2];ReturnValue.Field[System.ValueTuple<,,,,,,>.Item3];value;manual",
- "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[3];ReturnValue.Field[System.ValueTuple<,,,,,,>.Item4];value;manual",
- "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[4];ReturnValue.Field[System.ValueTuple<,,,,,,>.Item5];value;manual",
- "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[5];ReturnValue.Field[System.ValueTuple<,,,,,,>.Item6];value;manual",
- "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[6];ReturnValue.Field[System.ValueTuple<,,,,,,>.Item7];value;manual",
+ "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[0];Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item1];value;manual",
+ "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[1];Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item2];value;manual",
+ "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[2];Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item3];value;manual",
+ "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[3];Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item4];value;manual",
+ "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[4];Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item5];value;manual",
+ "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[5];Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item6];value;manual",
+ "System;ValueTuple<,,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6,T7);;Argument[6];Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item7];value;manual",
"System;ValueTuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item1];ReturnValue;value;manual",
"System;ValueTuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item2];ReturnValue;value;manual",
"System;ValueTuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item3];ReturnValue;value;manual",
@@ -1650,47 +1650,47 @@ private class SystemValueTupleTFlowModelCsv extends SummaryModelCsv {
"System;ValueTuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item5];ReturnValue;value;manual",
"System;ValueTuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item6];ReturnValue;value;manual",
"System;ValueTuple<,,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,,>.Item7];ReturnValue;value;manual",
- "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[0];ReturnValue.Field[System.ValueTuple<,,,,,>.Item1];value;manual",
- "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[1];ReturnValue.Field[System.ValueTuple<,,,,,>.Item2];value;manual",
- "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[2];ReturnValue.Field[System.ValueTuple<,,,,,>.Item3];value;manual",
- "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[3];ReturnValue.Field[System.ValueTuple<,,,,,>.Item4];value;manual",
- "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[4];ReturnValue.Field[System.ValueTuple<,,,,,>.Item5];value;manual",
- "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[5];ReturnValue.Field[System.ValueTuple<,,,,,>.Item6];value;manual",
+ "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[0];Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item1];value;manual",
+ "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[1];Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item2];value;manual",
+ "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[2];Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item3];value;manual",
+ "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[3];Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item4];value;manual",
+ "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[4];Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item5];value;manual",
+ "System;ValueTuple<,,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5,T6);;Argument[5];Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item6];value;manual",
"System;ValueTuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item1];ReturnValue;value;manual",
"System;ValueTuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item2];ReturnValue;value;manual",
"System;ValueTuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item3];ReturnValue;value;manual",
"System;ValueTuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item4];ReturnValue;value;manual",
"System;ValueTuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item5];ReturnValue;value;manual",
"System;ValueTuple<,,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,,>.Item6];ReturnValue;value;manual",
- "System;ValueTuple<,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5);;Argument[0];ReturnValue.Field[System.ValueTuple<,,,,>.Item1];value;manual",
- "System;ValueTuple<,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5);;Argument[1];ReturnValue.Field[System.ValueTuple<,,,,>.Item2];value;manual",
- "System;ValueTuple<,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5);;Argument[2];ReturnValue.Field[System.ValueTuple<,,,,>.Item3];value;manual",
- "System;ValueTuple<,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5);;Argument[3];ReturnValue.Field[System.ValueTuple<,,,,>.Item4];value;manual",
- "System;ValueTuple<,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5);;Argument[4];ReturnValue.Field[System.ValueTuple<,,,,>.Item5];value;manual",
+ "System;ValueTuple<,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5);;Argument[0];Argument[Qualifier].Field[System.ValueTuple<,,,,>.Item1];value;manual",
+ "System;ValueTuple<,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5);;Argument[1];Argument[Qualifier].Field[System.ValueTuple<,,,,>.Item2];value;manual",
+ "System;ValueTuple<,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5);;Argument[2];Argument[Qualifier].Field[System.ValueTuple<,,,,>.Item3];value;manual",
+ "System;ValueTuple<,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5);;Argument[3];Argument[Qualifier].Field[System.ValueTuple<,,,,>.Item4];value;manual",
+ "System;ValueTuple<,,,,>;false;ValueTuple;(T1,T2,T3,T4,T5);;Argument[4];Argument[Qualifier].Field[System.ValueTuple<,,,,>.Item5];value;manual",
"System;ValueTuple<,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,>.Item1];ReturnValue;value;manual",
"System;ValueTuple<,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,>.Item2];ReturnValue;value;manual",
"System;ValueTuple<,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,>.Item3];ReturnValue;value;manual",
"System;ValueTuple<,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,>.Item4];ReturnValue;value;manual",
"System;ValueTuple<,,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,,>.Item5];ReturnValue;value;manual",
- "System;ValueTuple<,,,>;false;ValueTuple;(T1,T2,T3,T4);;Argument[0];ReturnValue.Field[System.ValueTuple<,,,>.Item1];value;manual",
- "System;ValueTuple<,,,>;false;ValueTuple;(T1,T2,T3,T4);;Argument[1];ReturnValue.Field[System.ValueTuple<,,,>.Item2];value;manual",
- "System;ValueTuple<,,,>;false;ValueTuple;(T1,T2,T3,T4);;Argument[2];ReturnValue.Field[System.ValueTuple<,,,>.Item3];value;manual",
- "System;ValueTuple<,,,>;false;ValueTuple;(T1,T2,T3,T4);;Argument[3];ReturnValue.Field[System.ValueTuple<,,,>.Item4];value;manual",
+ "System;ValueTuple<,,,>;false;ValueTuple;(T1,T2,T3,T4);;Argument[0];Argument[Qualifier].Field[System.ValueTuple<,,,>.Item1];value;manual",
+ "System;ValueTuple<,,,>;false;ValueTuple;(T1,T2,T3,T4);;Argument[1];Argument[Qualifier].Field[System.ValueTuple<,,,>.Item2];value;manual",
+ "System;ValueTuple<,,,>;false;ValueTuple;(T1,T2,T3,T4);;Argument[2];Argument[Qualifier].Field[System.ValueTuple<,,,>.Item3];value;manual",
+ "System;ValueTuple<,,,>;false;ValueTuple;(T1,T2,T3,T4);;Argument[3];Argument[Qualifier].Field[System.ValueTuple<,,,>.Item4];value;manual",
"System;ValueTuple<,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,>.Item1];ReturnValue;value;manual",
"System;ValueTuple<,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,>.Item2];ReturnValue;value;manual",
"System;ValueTuple<,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,>.Item3];ReturnValue;value;manual",
"System;ValueTuple<,,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,,>.Item4];ReturnValue;value;manual",
- "System;ValueTuple<,,>;false;ValueTuple;(T1,T2,T3);;Argument[0];ReturnValue.Field[System.ValueTuple<,,>.Item1];value;manual",
- "System;ValueTuple<,,>;false;ValueTuple;(T1,T2,T3);;Argument[1];ReturnValue.Field[System.ValueTuple<,,>.Item2];value;manual",
- "System;ValueTuple<,,>;false;ValueTuple;(T1,T2,T3);;Argument[2];ReturnValue.Field[System.ValueTuple<,,>.Item3];value;manual",
+ "System;ValueTuple<,,>;false;ValueTuple;(T1,T2,T3);;Argument[0];Argument[Qualifier].Field[System.ValueTuple<,,>.Item1];value;manual",
+ "System;ValueTuple<,,>;false;ValueTuple;(T1,T2,T3);;Argument[1];Argument[Qualifier].Field[System.ValueTuple<,,>.Item2];value;manual",
+ "System;ValueTuple<,,>;false;ValueTuple;(T1,T2,T3);;Argument[2];Argument[Qualifier].Field[System.ValueTuple<,,>.Item3];value;manual",
"System;ValueTuple<,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,>.Item1];ReturnValue;value;manual",
"System;ValueTuple<,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,>.Item2];ReturnValue;value;manual",
"System;ValueTuple<,,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,,>.Item3];ReturnValue;value;manual",
- "System;ValueTuple<,>;false;ValueTuple;(T1,T2);;Argument[0];ReturnValue.Field[System.ValueTuple<,>.Item1];value;manual",
- "System;ValueTuple<,>;false;ValueTuple;(T1,T2);;Argument[1];ReturnValue.Field[System.ValueTuple<,>.Item2];value;manual",
+ "System;ValueTuple<,>;false;ValueTuple;(T1,T2);;Argument[0];Argument[Qualifier].Field[System.ValueTuple<,>.Item1];value;manual",
+ "System;ValueTuple<,>;false;ValueTuple;(T1,T2);;Argument[1];Argument[Qualifier].Field[System.ValueTuple<,>.Item2];value;manual",
"System;ValueTuple<,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,>.Item1];ReturnValue;value;manual",
"System;ValueTuple<,>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<,>.Item2];ReturnValue;value;manual",
- "System;ValueTuple<>;false;ValueTuple;(T1);;Argument[0];ReturnValue.Field[System.ValueTuple<>.Item1];value;manual",
+ "System;ValueTuple<>;false;ValueTuple;(T1);;Argument[0];Argument[Qualifier].Field[System.ValueTuple<>.Item1];value;manual",
"System;ValueTuple<>;false;get_Item;(System.Int32);;Argument[Qualifier].Field[System.ValueTuple<>.Item1];ReturnValue;value;manual",
]
}
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll
index b3f247ede94..f4c5596c973 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll
@@ -1,11 +1,23 @@
/**
* THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT.
- * Definitions of taint steps in the dotnetruntime framework.
+ * Definitions of taint steps in the Runtime framework.
*/
import csharp
private import semmle.code.csharp.dataflow.ExternalFlow
+private class RuntimeSinksCsv extends SinkModelCsv {
+ override predicate row(string row) {
+ row =
+ [
+ "System.Data.Odbc;OdbcDataAdapter;false;OdbcDataAdapter;(System.String,System.Data.Odbc.OdbcConnection);;Argument[0];sql;generated",
+ "System.Data.Odbc;OdbcDataAdapter;false;OdbcDataAdapter;(System.String,System.String);;Argument[0];sql;generated",
+ "System.Net.Http;StringContent;false;StringContent;(System.String);;Argument[0];xss;generated",
+ "System.Net.Http;StringContent;false;StringContent;(System.String,System.Text.Encoding);;Argument[0];xss;generated"
+ ]
+ }
+}
+
private class RuntimeSummaryCsv extends SummaryModelCsv {
override predicate row(string row) {
row =
@@ -529,11 +541,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.CodeDom.Compiler;GeneratedCodeAttribute;false;GeneratedCodeAttribute;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated",
"System.CodeDom.Compiler;GeneratedCodeAttribute;false;get_Tool;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.CodeDom.Compiler;GeneratedCodeAttribute;false;get_Version;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.CodeDom.Compiler;IndentedTextWriter;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.CodeDom.Compiler;IndentedTextWriter;false;IndentedTextWriter;(System.IO.TextWriter,System.String);;Argument[0];Argument[Qualifier];taint;generated",
"System.CodeDom.Compiler;IndentedTextWriter;false;IndentedTextWriter;(System.IO.TextWriter,System.String);;Argument[1];Argument[Qualifier];taint;generated",
- "System.CodeDom.Compiler;IndentedTextWriter;false;WriteLineNoTabsAsync;(System.String);;Argument[0];ReturnValue;taint;generated",
- "System.CodeDom.Compiler;IndentedTextWriter;false;WriteLineNoTabsAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated",
"System.CodeDom.Compiler;IndentedTextWriter;false;get_Encoding;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.CodeDom.Compiler;IndentedTextWriter;false;get_InnerWriter;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.CodeDom.Compiler;IndentedTextWriter;false;get_NewLine;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -746,7 +755,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.CodeDom;CodeNamespaceImport;false;set_Namespace;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
"System.CodeDom;CodeNamespaceImportCollection;false;Add;(System.CodeDom.CodeNamespaceImport);;Argument[0];Argument[Qualifier];taint;generated",
"System.CodeDom;CodeNamespaceImportCollection;false;AddRange;(System.CodeDom.CodeNamespaceImport[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
- "System.CodeDom;CodeNamespaceImportCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.CodeDom;CodeNamespaceImportCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.CodeDom;CodeNamespaceImportCollection;false;set_Item;(System.Int32,System.CodeDom.CodeNamespaceImport);;Argument[1];Argument[Qualifier];taint;generated",
"System.CodeDom;CodeObject;false;get_UserData;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -919,7 +927,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Concurrent;ConcurrentBag<>;false;TryAdd;(T);;Argument[0];Argument[Qualifier];taint;generated",
"System.Collections.Concurrent;ConcurrentBag<>;false;TryPeek;(T);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Concurrent;ConcurrentBag<>;false;TryTake;(T);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetOrAdd;(TKey,TValue);;Argument[1];ReturnValue;taint;generated",
"System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Concurrent;ConcurrentStack<>;false;ConcurrentStack;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated",
@@ -933,7 +940,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Concurrent;Partitioner;false;Create<>;(System.Collections.Generic.IEnumerable,System.Collections.Concurrent.EnumerablePartitionerOptions);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Concurrent;Partitioner;false;Create<>;(System.Collections.Generic.IList,System.Boolean);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Concurrent;Partitioner;false;Create<>;(TSource[],System.Boolean);;Argument[0].Element;ReturnValue;taint;generated",
- "System.Collections.Generic;CollectionExtensions;false;AsReadOnly<,>;(System.Collections.Generic.IDictionary);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Generic;CollectionExtensions;false;AsReadOnly<>;(System.Collections.Generic.IList);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Generic;CollectionExtensions;false;GetDefaultAssets;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Generic;CollectionExtensions;false;GetDefaultGroup;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated",
@@ -945,9 +951,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[1];ReturnValue;taint;generated",
"System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated",
"System.Collections.Generic;CollectionExtensions;false;Remove<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;ReturnValue;taint;generated",
- "System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;Argument[2];taint;generated",
- "System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[1];Argument[0].Element;taint;generated",
- "System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[2];Argument[0].Element;taint;generated",
"System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Entry;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Key;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -967,8 +970,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Generic;HashSet<>;false;HashSet;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated",
"System.Collections.Generic;HashSet<>;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Generic;HashSet<>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[0];ReturnValue;taint;generated",
- "System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[1];ReturnValue;taint;generated",
"System.Collections.Generic;KeyValuePair<,>;false;Deconstruct;(TKey,TValue);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Generic;KeyValuePair<,>;false;get_Key;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Generic;KeyValuePair<,>;false;get_Value;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -1120,12 +1121,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableDictionary;false;GetValueOrDefault<,>;(System.Collections.Immutable.IImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[0].Element;ReturnValue;taint;generated",
@@ -1142,7 +1137,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableDictionary<,>+Builder;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableDictionary<,>+Builder;false;set_KeyComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated",
"System.Collections.Immutable;ImmutableDictionary<,>+Builder;false;set_ValueComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Collections.Immutable;ImmutableDictionary<,>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableDictionary<,>;false;Remove;(TKey);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableDictionary<,>;false;RemoveRange;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableDictionary<,>;false;SetItem;(TKey,TValue);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -1155,8 +1149,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableDictionary<,>;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableDictionary<,>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated",
"System.Collections.Immutable;ImmutableDictionary<,>;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableHashSet;false;Create<>;(System.Collections.Generic.IEqualityComparer,T);;Argument[1];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableHashSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableHashSet;false;ToImmutableHashSet<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated",
@@ -1166,7 +1158,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableHashSet<>+Builder;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableHashSet<>+Builder;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableHashSet<>+Builder;false;set_KeyComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Collections.Immutable;ImmutableHashSet<>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableHashSet<>;false;Except;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableHashSet<>;false;Intersect;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableHashSet<>;false;Remove;(T);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -1179,9 +1170,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableHashSet<>;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableHashSet<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated",
"System.Collections.Immutable;ImmutableInterlocked;false;GetOrAdd<,>;(System.Collections.Immutable.ImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableList;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableList;false;Create<>;(T[]);;Argument[0].Element;ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableList;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableList;false;Remove<>;(System.Collections.Immutable.IImmutableList,T);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableList;false;RemoveRange<>;(System.Collections.Immutable.IImmutableList,System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableList;false;Replace<>;(System.Collections.Immutable.IImmutableList,T,T);;Argument[0].Element;ReturnValue;taint;generated",
@@ -1240,12 +1228,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[1];ReturnValue;taint;generated",
@@ -1262,8 +1244,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_KeyComparer;(System.Collections.Generic.IComparer);;Argument[0];Argument[Qualifier];taint;generated",
"System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_ValueComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Remove;(TKey);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedDictionary<,>;false;RemoveRange;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedDictionary<,>;false;SetItem;(TKey,TValue);;Argument[0];ReturnValue;taint;generated",
@@ -1281,10 +1261,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableSortedDictionary<,>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated",
"System.Collections.Immutable;ImmutableSortedDictionary<,>;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[0];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[1];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T[]);;Argument[0];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet;false;CreateBuilder<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated",
@@ -1294,7 +1271,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableSortedSet;false;ToImmutableSortedSet<>;(System.Collections.Generic.IEnumerable,System.Collections.Generic.IComparer);;Argument[1];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet;false;ToImmutableSortedSet<>;(System.Collections.Immutable.ImmutableSortedSet+Builder);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;IntersectWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated",
- "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;SymmetricExceptWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;ToImmutable;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -1305,13 +1281,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;set_KeyComparer;(System.Collections.Generic.IComparer);;Argument[0];Argument[Qualifier];taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedSet<>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>;false;Except;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedSet<>;false;Intersect;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>;false;Remove;(T);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated",
- "System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated",
- "System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>;false;ToBuilder;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated",
"System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -1335,23 +1306,17 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections.Immutable;ImmutableStack<>;false;Push;(T);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.ObjectModel;Collection<>;false;Collection;(System.Collections.Generic.IList);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated",
- "System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated",
"System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated",
- "System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated",
"System.Collections.ObjectModel;Collection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.ObjectModel;Collection<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated",
- "System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated",
"System.Collections.ObjectModel;KeyedCollection<,>;false;KeyedCollection;(System.Collections.Generic.IEqualityComparer,System.Int32);;Argument[0];Argument[Qualifier];taint;generated",
"System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated",
- "System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated",
"System.Collections.ObjectModel;KeyedCollection<,>;false;TryGetValue;(TKey,TItem);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.ObjectModel;KeyedCollection<,>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.ObjectModel;KeyedCollection<,>;false;get_Dictionary;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated",
- "System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated",
"System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated",
- "System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated",
"System.Collections.ObjectModel;ReadOnlyCollection<>;false;ReadOnlyCollection;(System.Collections.Generic.IList);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Collections.ObjectModel;ReadOnlyCollection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections.ObjectModel;ReadOnlyCollection<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -1430,7 +1395,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections;BitArray;false;Xor;(System.Collections.BitArray);;Argument[Qualifier];ReturnValue;value;generated",
"System.Collections;BitArray;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated",
"System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[Qualifier];Argument[0];taint;generated",
"System.Collections;CollectionBase;false;get_InnerList;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections;CollectionBase;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections;CollectionBase;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -1480,7 +1444,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Collections;Stack;false;Synchronized;(System.Collections.Stack);;Argument[0].Element;ReturnValue;taint;generated",
"System.Collections;Stack;false;ToArray;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Collections;Stack;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated",
- "System.ComponentModel.Composition.Hosting;AggregateCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;AggregateCatalog;false;get_Catalogs;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;AggregateExportProvider;false;AggregateExportProvider;(System.ComponentModel.Composition.Hosting.ExportProvider[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;AggregateExportProvider;false;GetExportsCore;(System.ComponentModel.Composition.Primitives.ImportDefinition,System.ComponentModel.Composition.Hosting.AtomicComposition);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -1489,7 +1452,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;ApplicationCatalog;(System.Reflection.ReflectionContext);;Argument[0];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;ApplicationCatalog;(System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[0];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;ApplicationCatalog;(System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated",
- "System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.Reflection.Assembly);;Argument[0];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.Reflection.Assembly,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[0];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.Reflection.Assembly,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated",
@@ -1502,7 +1464,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.String,System.Reflection.ReflectionContext);;Argument[1];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[2];Argument[Qualifier];taint;generated",
- "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;get_Assembly;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;get_DisplayName;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -1539,7 +1500,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;CompositionScopeDefinition;(System.ComponentModel.Composition.Primitives.ComposablePartCatalog,System.Collections.Generic.IEnumerable,System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;CompositionScopeDefinition;(System.ComponentModel.Composition.Primitives.ComposablePartCatalog,System.Collections.Generic.IEnumerable,System.Collections.Generic.IEnumerable);;Argument[1].Element;Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;CompositionScopeDefinition;(System.ComponentModel.Composition.Primitives.ComposablePartCatalog,System.Collections.Generic.IEnumerable,System.Collections.Generic.IEnumerable);;Argument[2].Element;Argument[Qualifier];taint;generated",
- "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;get_Children;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;get_PublicSurface;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated",
@@ -1554,7 +1514,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[2];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[3];Argument[Qualifier];taint;generated",
- "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;get_DisplayName;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;get_FullPath;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -1573,14 +1532,12 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;false;get_AddedExports;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;false;get_ChangedContractNames;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;false;get_RemovedExports;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.ComponentModel.Composition.Hosting;FilteredCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;FilteredCatalog;false;get_Complement;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Hosting;ImportEngine;false;ImportEngine;(System.ComponentModel.Composition.Hosting.ExportProvider,System.ComponentModel.Composition.Hosting.CompositionOptions);;Argument[0];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[2];Argument[Qualifier];taint;generated",
- "System.ComponentModel.Composition.Primitives;ComposablePartCatalog;true;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Primitives;ComposablePartCatalog;true;get_Parts;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel.Composition.Primitives;ComposablePartException;false;ComposablePartException;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[0];Argument[Qualifier];taint;generated",
"System.ComponentModel.Composition.Primitives;ComposablePartException;false;ComposablePartException;(System.String,System.ComponentModel.Composition.Primitives.ICompositionElement,System.Exception);;Argument[1];Argument[Qualifier];taint;generated",
@@ -1781,9 +1738,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[1];ReturnValue;taint;generated",
"System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated",
"System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated",
- "System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated",
"System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated",
- "System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated",
"System.ComponentModel;CategoryAttribute;false;CategoryAttribute;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
"System.ComponentModel;CategoryAttribute;false;get_Category;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel;CharConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated",
@@ -1917,7 +1872,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.ComponentModel;ToolboxItemAttribute;false;get_ToolboxItemTypeName;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel;ToolboxItemFilterAttribute;false;get_TypeId;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel;TypeConverter+StandardValuesCollection;false;CopyTo;(System.Array,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated",
- "System.ComponentModel;TypeConverter+StandardValuesCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel;TypeConverter+StandardValuesCollection;false;StandardValuesCollection;(System.Collections.ICollection);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.ComponentModel;TypeConverter+StandardValuesCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.ComponentModel;TypeConverter;false;ConvertFrom;(System.Object);;Argument[0];ReturnValue;taint;generated",
@@ -2380,10 +2334,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Data.Common;DataTableMappingCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated",
"System.Data.Common;DbCommand;false;ExecuteReader;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbCommand;false;ExecuteReader;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data.Common;DbCommand;false;ExecuteReaderAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbCommand;false;get_Connection;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbCommand;false;get_Parameters;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbCommand;false;get_Transaction;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -2391,7 +2341,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Data.Common;DbCommand;false;set_Connection;(System.Data.IDbConnection);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data.Common;DbCommand;false;set_Transaction;(System.Data.Common.DbTransaction);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data.Common;DbCommand;false;set_Transaction;(System.Data.IDbTransaction);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Data.Common;DbCommand;true;ExecuteDbDataReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbCommand;true;PrepareAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -2418,7 +2367,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String);;Argument[2];Argument[0];taint;generated",
"System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[1];Argument[0];taint;generated",
"System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[2];Argument[0];taint;generated",
- "System.Data.Common;DbConnectionStringBuilder;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbConnectionStringBuilder;false;GetProperties;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbConnectionStringBuilder;false;GetProperties;(System.Attribute[]);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbConnectionStringBuilder;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated",
@@ -2445,12 +2393,9 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[0];ReturnValue;taint;generated",
"System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[1];ReturnValue;taint;generated",
"System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[3];ReturnValue;taint;generated",
- "System.Data.Common;DbDataReader;false;GetFieldValueAsync<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbDataReader;true;GetFieldValue<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data.Common;DbDataReader;true;GetFieldValueAsync<>;(System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbDataReader;true;GetProviderSpecificValue;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbDataReader;true;GetProviderSpecificValues;(System.Object[]);;Argument[Qualifier];Argument[0].Element;taint;generated",
- "System.Data.Common;DbDataReader;true;GetSchemaTableAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbDataReader;true;GetTextReader;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Common;DbDataRecord;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated",
"System.Data.Common;DbEnumerator;false;DbEnumerator;(System.Data.IDataReader);;Argument[0];Argument[Qualifier];taint;generated",
@@ -2595,7 +2540,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Data.Odbc;OdbcParameter;false;set_Value;(System.Object);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data.Odbc;OdbcParameterCollection;false;Add;(System.Data.Odbc.OdbcParameter);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data.Odbc;OdbcParameterCollection;false;Add;(System.Data.Odbc.OdbcParameter);;Argument[0];ReturnValue;taint;generated",
- "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.Data.Odbc.OdbcParameter);;Argument[Qualifier];Argument[0];taint;generated",
"System.Data.Odbc;OdbcParameterCollection;false;Add;(System.String,System.Data.Odbc.OdbcType);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data.Odbc;OdbcParameterCollection;false;Add;(System.String,System.Data.Odbc.OdbcType);;Argument[0];ReturnValue;taint;generated",
"System.Data.Odbc;OdbcParameterCollection;false;Add;(System.String,System.Data.Odbc.OdbcType,System.Int32);;Argument[0];Argument[Qualifier];taint;generated",
@@ -2617,7 +2561,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Data.Odbc;OdbcParameterCollection;false;GetParameter;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Odbc;OdbcParameterCollection;false;GetParameter;(System.String);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data.Odbc;OdbcParameterCollection;false;Insert;(System.Int32,System.Data.Odbc.OdbcParameter);;Argument[1];Argument[Qualifier];taint;generated",
- "System.Data.Odbc;OdbcParameterCollection;false;Insert;(System.Int32,System.Data.Odbc.OdbcParameter);;Argument[Qualifier];Argument[1];taint;generated",
"System.Data.Odbc;OdbcParameterCollection;false;SetParameter;(System.Int32,System.Data.Common.DbParameter);;Argument[Qualifier];Argument[1];taint;generated",
"System.Data.Odbc;OdbcParameterCollection;false;SetParameter;(System.String,System.Data.Common.DbParameter);;Argument[Qualifier];Argument[1];taint;generated",
"System.Data.Odbc;OdbcParameterCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -2729,15 +2672,11 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Data;DataColumn;false;set_Prefix;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data;DataColumnChangeEventArgs;false;DataColumnChangeEventArgs;(System.Data.DataRow,System.Data.DataColumn,System.Object);;Argument[1];Argument[Qualifier];taint;generated",
"System.Data;DataColumnChangeEventArgs;false;get_Column;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data;DataColumnCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data;DataColumnCollection;false;Add;(System.String,System.Type);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data;DataColumnCollection;false;Add;(System.String,System.Type,System.String);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataColumnCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataColumnCollection;false;get_Item;(System.String);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataColumnCollection;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataReaderExtensions;false;GetDateTime;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated",
"System.Data;DataReaderExtensions;false;GetFieldValue<>;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated",
- "System.Data;DataReaderExtensions;false;GetFieldValueAsync<>;(System.Data.Common.DbDataReader,System.String,System.Threading.CancellationToken);;Argument[0].Element;ReturnValue;taint;generated",
"System.Data;DataReaderExtensions;false;GetGuid;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated",
"System.Data;DataReaderExtensions;false;GetProviderSpecificValue;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated",
"System.Data;DataReaderExtensions;false;GetString;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated",
@@ -2767,16 +2706,10 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Data;DataRelation;false;get_RelationName;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataRelation;false;set_RelationName;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data;DataRelationCollection;false;Remove;(System.Data.DataRelation);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];ReturnValue;taint;generated",
- "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];ReturnValue;taint;generated",
- "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataRow;false;DataRow;(System.Data.DataRowBuilder);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data;DataRow;false;GetChildRows;(System.Data.DataRelation);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataRow;false;GetChildRows;(System.Data.DataRelation,System.Data.DataRowVersion);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -2874,10 +2807,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Data;DataTable;false;set_PrimaryKey;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Data;DataTable;false;set_Site;(System.ComponentModel.ISite);;Argument[0];Argument[Qualifier];taint;generated",
"System.Data;DataTable;false;set_TableName;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Data;DataTableCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated",
"System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];ReturnValue;taint;generated",
- "System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataTableCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataTableCollection;false;get_Item;(System.String);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Data;DataTableCollection;false;get_Item;(System.String,System.String);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -3180,29 +3111,20 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.DirectoryServices.Protocols;DirSyncRequestControl;false;DirSyncRequestControl;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.DirectoryServices.Protocols;DirSyncRequestControl;false;set_Cookie;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
- "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Byte[]);;Argument[Qualifier];Argument[0].Element;taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
- "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.String);;Argument[Qualifier];Argument[0];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Uri);;Argument[0];Argument[Qualifier];taint;generated",
- "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Uri);;Argument[Qualifier];Argument[0];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;AddRange;(System.Object[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;CopyTo;(System.Object[],System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;DirectoryAttribute;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;DirectoryAttribute;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated",
- "System.DirectoryServices.Protocols;DirectoryAttribute;false;DirectoryAttribute;(System.String,System.Object[]);;Argument[Qualifier];Argument[1].Element;taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;GetValues;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Byte[]);;Argument[1].Element;Argument[Qualifier];taint;generated",
- "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Byte[]);;Argument[Qualifier];Argument[1].Element;taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.String);;Argument[1];Argument[Qualifier];taint;generated",
- "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.String);;Argument[Qualifier];Argument[1];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Uri);;Argument[1];Argument[Qualifier];taint;generated",
- "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Uri);;Argument[Qualifier];Argument[1];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;Remove;(System.Object);;Argument[0];Argument[Qualifier];taint;generated",
- "System.DirectoryServices.Protocols;DirectoryAttribute;false;Remove;(System.Object);;Argument[Qualifier];Argument[0];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;get_Name;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;set_Item;(System.Int32,System.Object);;Argument[1];Argument[Qualifier];taint;generated",
- "System.DirectoryServices.Protocols;DirectoryAttribute;false;set_Item;(System.Int32,System.Object);;Argument[Qualifier];Argument[1];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttribute;false;set_Name;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttributeCollection;false;Add;(System.DirectoryServices.Protocols.DirectoryAttribute);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.DirectoryServices.Protocols;DirectoryAttributeCollection;false;AddRange;(System.DirectoryServices.Protocols.DirectoryAttributeCollection);;Argument[0].Element;Argument[Qualifier];taint;generated",
@@ -3391,22 +3313,18 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Drawing.Printing;PrintPageEventArgs;false;get_PageBounds;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Drawing.Printing;PrintPageEventArgs;false;get_PageSettings;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;Add;(System.Drawing.Printing.PaperSize);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;PaperSizeCollection;(System.Drawing.Printing.PaperSize[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated",
"System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;Add;(System.Drawing.Printing.PaperSource);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;PaperSourceCollection;(System.Drawing.Printing.PaperSource[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated",
"System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;Add;(System.Drawing.Printing.PrinterResolution);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;PrinterResolutionCollection;(System.Drawing.Printing.PrinterResolution[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated",
"System.Drawing.Printing;PrinterSettings+StringCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Drawing.Printing;PrinterSettings+StringCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Drawing.Printing;PrinterSettings+StringCollection;false;StringCollection;(System.String[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Drawing.Printing;PrinterSettings+StringCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Drawing.Printing;PrinterSettings+StringCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated",
@@ -3663,14 +3581,10 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.IO.Compression;BrotliStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.IO.Compression;BrotliStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO.Compression;DeflateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
- "System.IO.Compression;DeflateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO.Compression;DeflateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO.Compression;DeflateStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO.Compression;GZipStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.IO.Compression;GZipStream;false;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionLevel,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO.Compression;GZipStream;false;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated",
- "System.IO.Compression;GZipStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO.Compression;GZipStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO.Compression;GZipStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO.Compression;ZLibException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated",
"System.IO.Compression;ZLibException;false;ZLibException;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[0];Argument[Qualifier];taint;generated",
@@ -3709,7 +3623,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.IO.IsolatedStorage;IsolatedStorage;false;get_AssemblyIdentity;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO.IsolatedStorage;IsolatedStorage;false;get_DomainIdentity;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
- "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO.IsolatedStorage;IsolatedStorageFileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
@@ -3830,7 +3743,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.IO;BinaryReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated",
- "System.IO;BinaryWriter;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;BinaryWriter;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.IO;BinaryWriter;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.IO;BinaryWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -3899,7 +3811,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.IO;FileNotFoundException;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;FileNotFoundException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
- "System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;FileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
@@ -3956,26 +3867,14 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.IO;RenamedEventArgs;false;RenamedEventArgs;(System.IO.WatcherChangeTypes,System.String,System.String,System.String);;Argument[3];Argument[Qualifier];taint;generated",
"System.IO;RenamedEventArgs;false;get_OldFullPath;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;RenamedEventArgs;false;get_OldName;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;Stream;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;Stream;false;Synchronized;(System.IO.Stream);;Argument[0];ReturnValue;taint;generated",
- "System.IO;Stream;true;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;Stream;true;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;Stream;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated",
"System.IO;StreamReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;StreamReader;false;get_CurrentEncoding;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;StreamWriter;false;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;StreamWriter;false;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated",
- "System.IO;StreamWriter;false;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;StreamWriter;false;WriteLine;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;StreamWriter;false;WriteLine;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated",
"System.IO;StreamWriter;false;WriteLine;(System.String,System.Object,System.Object);;Argument[0];Argument[Qualifier];taint;generated",
@@ -3987,15 +3886,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.IO;StreamWriter;false;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated",
"System.IO;StreamWriter;false;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;StreamWriter;false;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated",
- "System.IO;StreamWriter;false;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated",
- "System.IO;StreamWriter;false;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;StreamWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;StreamWriter;false;get_Encoding;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;StringWriter;false;GetStringBuilder;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -4003,29 +3894,19 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.IO;StringWriter;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;StringWriter;false;Write;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.IO;StringWriter;false;Write;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
- "System.IO;StringWriter;false;Write;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.IO;StringWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
- "System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
- "System.IO;StringWriter;false;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.IO;StringWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
- "System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
- "System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;TextReader;false;Synchronized;(System.IO.TextReader);;Argument[0];ReturnValue;taint;generated",
"System.IO;TextWriter;false;Synchronized;(System.IO.TextWriter);;Argument[0];ReturnValue;taint;generated",
"System.IO;TextWriter;false;TextWriter;(System.IFormatProvider);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
- "System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated",
- "System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
- "System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated",
- "System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;TextWriter;true;Write;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.IO;TextWriter;true;Write;(System.Object);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;TextWriter;true;Write;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated",
@@ -4039,14 +3920,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.IO;TextWriter;true;Write;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated",
"System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated",
- "System.IO;TextWriter;true;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;TextWriter;true;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.IO;TextWriter;true;WriteLine;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.IO;TextWriter;true;WriteLine;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated",
@@ -4063,18 +3937,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.IO;TextWriter;true;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated",
"System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated",
"System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated",
- "System.IO;TextWriter;true;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated",
- "System.IO;TextWriter;true;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
- "System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;TextWriter;true;get_FormatProvider;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;TextWriter;true;get_NewLine;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.IO;TextWriter;true;set_NewLine;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
@@ -4673,7 +4537,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Http.Headers;HeaderStringValues;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http.Headers;HttpHeaders;false;get_NonValidated;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http.Headers;HttpHeadersNonValidated+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Net.Http.Headers;HttpHeadersNonValidated;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http.Headers;HttpHeadersNonValidated;false;TryGetValue;(System.String,System.Net.Http.Headers.HeaderStringValues);;Argument[0];ReturnValue;taint;generated",
"System.Net.Http.Headers;HttpHeadersNonValidated;false;TryGetValues;(System.String,System.Net.Http.Headers.HeaderStringValues);;Argument[0];ReturnValue;taint;generated",
"System.Net.Http.Headers;HttpHeadersNonValidated;false;get_Item;(System.String);;Argument[0];ReturnValue;taint;generated",
@@ -4774,23 +4637,14 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Net.Http;ByteArrayContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Net.Http;ByteArrayContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;ByteArrayContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated",
- "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated",
"System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated",
- "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
- "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated",
"System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated",
- "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;DelegatingHandler;false;DelegatingHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated",
"System.Net.Http;DelegatingHandler;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.Net.Http;DelegatingHandler;false;get_InnerHandler;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;DelegatingHandler;false;set_InnerHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
- "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated",
"System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated",
- "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage);;Argument[Qualifier];Argument[0];taint;generated",
"System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption);;Argument[Qualifier];Argument[0];taint;generated",
"System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated",
@@ -4817,10 +4671,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Http;HttpContent;false;ReadAsStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;HttpContent;false;get_Headers;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;HttpContent;true;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Net.Http;HttpContent;true;CreateContentReadStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated",
- "System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;HttpMessageInvoker;false;HttpMessageInvoker;(System.Net.Http.HttpMessageHandler,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated",
"System.Net.Http;HttpMessageInvoker;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.Net.Http;HttpMethod;false;HttpMethod;(System.String);;Argument[0];Argument[Qualifier];taint;generated",
@@ -4855,7 +4706,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated",
"System.Net.Http;MultipartFormDataContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated",
"System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;ReadOnlyMemoryContent;false;ReadOnlyMemoryContent;(System.ReadOnlyMemory);;Argument[0];Argument[Qualifier];taint;generated",
"System.Net.Http;SocketsHttpConnectionContext;false;get_DnsEndPoint;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;SocketsHttpConnectionContext;false;get_InitialRequestMessage;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -4894,19 +4744,11 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_NegotiatedHttpVersion;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_PlaintextStream;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;StreamContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated",
- "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated",
"System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated",
- "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
- "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated",
"System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated",
- "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated",
"System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream,System.Int32);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
- "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated",
"System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated",
- "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String);;Argument[0];ReturnValue;taint;generated",
"System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[0];ReturnValue;taint;generated",
"System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[1];ReturnValue;taint;generated",
@@ -5039,7 +4881,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Quic;QuicListener;false;QuicListener;(System.Net.Quic.Implementations.QuicImplementationProvider,System.Net.Quic.QuicListenerOptions);;Argument[1];Argument[Qualifier];taint;generated",
"System.Net.Quic;QuicListener;false;get_ListenEndPoint;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Security;AuthenticatedStream;false;AuthenticatedStream;(System.IO.Stream,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated",
- "System.Net.Security;AuthenticatedStream;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Security;AuthenticatedStream;false;get_InnerStream;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[1];Argument[Qualifier];taint;generated",
"System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[2];Argument[Qualifier];taint;generated",
@@ -5058,7 +4899,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Security;NegotiateStream;false;AuthenticateAsServerAsync;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy,System.Net.Security.ProtectionLevel,System.Security.Principal.TokenImpersonationLevel);;Argument[1];Argument[Qualifier];taint;generated",
"System.Net.Security;NegotiateStream;false;AuthenticateAsServerAsync;(System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy);;Argument[0];Argument[Qualifier];taint;generated",
"System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
- "System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -5071,7 +4911,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Security;SslCertificateTrust;false;CreateForX509Collection;(System.Security.Cryptography.X509Certificates.X509Certificate2Collection,System.Boolean);;Argument[0].Element;ReturnValue;taint;generated",
"System.Net.Security;SslCertificateTrust;false;CreateForX509Store;(System.Security.Cryptography.X509Certificates.X509Store,System.Boolean);;Argument[0];ReturnValue;taint;generated",
"System.Net.Security;SslStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
- "System.Net.Security;SslStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Security;SslStream;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated",
"System.Net.Security;SslStream;false;get_LocalCertificate;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Security;SslStream;false;get_NegotiatedApplicationProtocol;();;Argument[Qualifier];ReturnValue;taint;generated",
@@ -5102,7 +4941,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Sockets;NetworkStream;false;get_Socket;();;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Sockets;SafeSocketHandle;false;SafeSocketHandle;(System.IntPtr,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated",
"System.Net.Sockets;Socket;false;Accept;();;Argument[Qualifier];ReturnValue;taint;generated",
- "System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket);;Argument[0];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -5126,7 +4964,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Sockets;Socket;false;DisconnectAsync;(System.Boolean,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;DisconnectAsync;(System.Boolean,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;DisconnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated",
- "System.Net.Sockets;Socket;false;EndAccept;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveAsync;(System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveAsync;(System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveAsync;(System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated",
@@ -5146,8 +4983,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Sockets;Socket;false;ReceiveFrom;(System.Span,System.Net.EndPoint);;Argument[1];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];Argument[Qualifier];taint;generated",
"System.Net.Sockets;Socket;false;ReceiveFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];ReturnValue;taint;generated",
- "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.ArraySegment,System.Net.EndPoint);;Argument[1];ReturnValue;taint;generated",
- "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated",
@@ -5161,8 +4996,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv {
"System.Net.Sockets;Socket;false;ReceiveMessageFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint,System.Net.Sockets.IPPacketInformation);;Argument[4];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveMessageFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint,System.Net.Sockets.IPPacketInformation);;Argument[2];Argument[Qualifier];taint;generated",
"System.Net.Sockets;Socket;false;ReceiveMessageFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint,System.Net.Sockets.IPPacketInformation);;Argument[2];ReturnValue;taint;generated",
- "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.ArraySegment,System.Net.EndPoint);;Argument[1];ReturnValue;taint;generated",
- "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated",
"System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.Memory