diff --git a/.devcontainer/swift/Dockerfile b/.devcontainer/swift/Dockerfile
new file mode 100644
index 00000000000..9b43eaf4f34
--- /dev/null
+++ b/.devcontainer/swift/Dockerfile
@@ -0,0 +1,9 @@
+# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.236.0/containers/cpp/.devcontainer/base.Dockerfile
+
+# [Choice] Debian / Ubuntu version (use Debian 11, Ubuntu 18.04/22.04 on local arm64/Apple Silicon): debian-11, debian-10, ubuntu-22.04, ubuntu-20.04, ubuntu-18.04
+FROM mcr.microsoft.com/vscode/devcontainers/cpp:0-ubuntu-22.04
+
+USER root
+ADD root.sh /tmp/root.sh
+ADD update-codeql.sh /usr/local/bin/update-codeql
+RUN bash /tmp/root.sh && rm /tmp/root.sh
diff --git a/.devcontainer/swift/devcontainer.json b/.devcontainer/swift/devcontainer.json
new file mode 100644
index 00000000000..97cd1bd022a
--- /dev/null
+++ b/.devcontainer/swift/devcontainer.json
@@ -0,0 +1,25 @@
+{
+ "extensions": [
+ "github.vscode-codeql",
+ "hbenl.vscode-test-explorer",
+ "ms-vscode.test-adapter-converter",
+ "slevesque.vscode-zipexplorer",
+ "ms-vscode.cpptools"
+ ],
+ "settings": {
+ "files.watcherExclude": {
+ "**/target/**": true
+ },
+ "codeQL.runningQueries.memory": 2048
+ },
+ "build": {
+ "dockerfile": "Dockerfile",
+ },
+ "runArgs": [
+ "--cap-add=SYS_PTRACE",
+ "--security-opt",
+ "seccomp=unconfined"
+ ],
+ "remoteUser": "vscode",
+ "onCreateCommand": ".devcontainer/swift/user.sh"
+}
diff --git a/.devcontainer/swift/root.sh b/.devcontainer/swift/root.sh
new file mode 100644
index 00000000000..5f9648ce3e8
--- /dev/null
+++ b/.devcontainer/swift/root.sh
@@ -0,0 +1,22 @@
+set -xe
+
+BAZELISK_VERSION=v1.12.0
+BAZELISK_DOWNLOAD_SHA=6b0bcb2ea15bca16fffabe6fda75803440375354c085480fe361d2cbf32501db
+
+apt-get update
+export DEBIAN_FRONTEND=noninteractive
+apt-get -y install --no-install-recommends \
+ zlib1g-dev \
+ uuid-dev \
+ python3-distutils \
+ python3-pip \
+ bash-completion
+
+# Install Bazel
+curl -fSsL -o /usr/local/bin/bazelisk https://github.com/bazelbuild/bazelisk/releases/download/${BAZELISK_VERSION}/bazelisk-linux-amd64
+echo "${BAZELISK_DOWNLOAD_SHA} */usr/local/bin/bazelisk" | sha256sum --check -
+chmod 0755 /usr/local/bin/bazelisk
+ln -s bazelisk /usr/local/bin/bazel
+
+# install latest codeql
+update-codeql
diff --git a/.devcontainer/swift/update-codeql.sh b/.devcontainer/swift/update-codeql.sh
new file mode 100755
index 00000000000..51fd7a612d3
--- /dev/null
+++ b/.devcontainer/swift/update-codeql.sh
@@ -0,0 +1,20 @@
+#!/bin/bash -e
+
+URL=https://github.com/github/codeql-cli-binaries/releases
+LATEST_VERSION=$(curl -L -s -H 'Accept: application/json' $URL/latest | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/')
+CURRENT_VERSION=v$(codeql version 2>/dev/null | sed -ne 's/.*release \([0-9.]*\)\./\1/p')
+if [[ $CURRENT_VERSION != $LATEST_VERSION ]]; then
+ if [[ $UID != 0 ]]; then
+ echo "update required, please run this script with sudo:"
+ echo " sudo $0"
+ exit 1
+ fi
+ ZIP=$(mktemp codeql.XXXX.zip)
+ curl -fSqL -o $ZIP $URL/download/$LATEST_VERSION/codeql-linux64.zip
+ unzip -q $ZIP -d /opt
+ rm $ZIP
+ ln -sf /opt/codeql/codeql /usr/local/bin/codeql
+ echo installed version $LATEST_VERSION
+else
+ echo current version $CURRENT_VERSION is up-to-date
+fi
diff --git a/.devcontainer/swift/user.sh b/.devcontainer/swift/user.sh
new file mode 100755
index 00000000000..fbf8aca57a7
--- /dev/null
+++ b/.devcontainer/swift/user.sh
@@ -0,0 +1,13 @@
+set -xe
+
+# add the workspace to the codeql search path
+mkdir -p /home/vscode/.config/codeql
+echo "--search-path /workspaces/codeql" > /home/vscode/.config/codeql/config
+
+# create a swift extractor pack with the current state
+cd /workspaces/codeql
+bazel run swift/create-extractor-pack
+
+#install and set up pre-commit
+python3 -m pip install pre-commit --no-warn-script-location
+$HOME/.local/bin/pre-commit install
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..ea216f68949 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: ./.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..1de2149ce2e 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: ./.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..e829957a0d3 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: ./.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..f5df6291b62 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: |
@@ -49,14 +49,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 +93,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
@@ -136,16 +122,8 @@ jobs:
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 +143,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 +155,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 d5d74af87c2..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
@@ -15,18 +16,22 @@ jobs:
- uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql
- uses: bazelbuild/setup-bazelisk@v2
+ - uses: actions/setup-python@v3
+ - uses: pre-commit/action@v3.0.0
+ name: Check that python code is properly formatted
+ with:
+ extra_args: autopep8 --all-files
- name: Run unit tests
run: |
bazel test //swift/codegen/test --test_output=errors
- - name: Check that QL generated code was checked in
- run: |
- bazel run //swift/codegen
- git add swift
- git diff --exit-code HEAD
+ - uses: pre-commit/action@v3.0.0
+ name: Check that QL generated code was checked in
+ with:
+ extra_args: swift-codegen --all-files
- name: Generate C++ files
run: |
- bazel run //swift/codegen:codegen -- --generate=trap,cpp --cpp-output=$PWD/swift-generated-headers
+ bazel run //swift/codegen:codegen -- --generate=trap,cpp --cpp-output=$PWD/swift-generated-cpp-files
- uses: actions/upload-artifact@v3
with:
- name: swift-generated-headers
- path: swift-generated-headers/*.h
+ name: swift-generated-cpp-files
+ path: swift-generated-cpp-files/**
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/.gitignore b/.gitignore
index 165d549cee2..7b8532b00d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,3 +58,6 @@ go/main
# node_modules folders except in the JS test suite
node_modules/
!/javascript/ql/test/**/node_modules/
+
+# Temporary folders for working with generated models
+.model-temp
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index d9beda01b10..d51681aa65c 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -15,6 +15,12 @@ repos:
- id: clang-format
files: ^swift/.*\.(h|c|cpp)$
+ - repo: https://github.com/pre-commit/mirrors-autopep8
+ rev: v1.6.0
+ hooks:
+ - id: autopep8
+ files: ^swift/codegen/.*\.py
+
- repo: local
hooks:
- id: codeql-format
@@ -40,7 +46,7 @@ repos:
name: Run Swift checked in code generation
files: ^swift/(codegen/|.*/generated/|ql/lib/(swift\.dbscheme$|codeql/swift/elements))
language: system
- entry: bazel run //swift/codegen
+ entry: bazel run //swift/codegen -- --quiet
pass_filenames: false
- id: swift-codegen-unit-tests
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 62472fc9ed8..403e6b91c2f 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -75,7 +75,8 @@
"DataFlow Java/C# Flow Summaries": [
"java/ql/lib/semmle/code/java/dataflow/internal/FlowSummaryImpl.qll",
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll",
- "ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll"
+ "ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll",
+ "swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImpl.qll"
],
"SsaReadPosition Java/C#": [
"java/ql/lib/semmle/code/java/dataflow/internal/rangeanalysis/SsaReadPositionCommon.qll",
@@ -391,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",
@@ -452,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",
@@ -527,7 +529,8 @@
"java/ql/lib/semmle/code/java/dataflow/internal/AccessPathSyntax.qll",
"javascript/ql/lib/semmle/javascript/frameworks/data/internal/AccessPathSyntax.qll",
"ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll",
- "python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll"
+ "python/ql/lib/semmle/python/frameworks/data/internal/AccessPathSyntax.qll",
+ "swift/ql/lib/codeql/swift/dataflow/internal/AccessPathSyntax.qll"
],
"IncompleteUrlSubstringSanitization": [
"javascript/ql/src/Security/CWE-020/IncompleteUrlSubstringSanitization.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/ql/lib/CHANGELOG.md b/cpp/ql/lib/CHANGELOG.md
index 0ad3f658760..9b4761ec2ce 100644
--- a/cpp/ql/lib/CHANGELOG.md
+++ b/cpp/ql/lib/CHANGELOG.md
@@ -1,3 +1,31 @@
+## 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
+
+* An `isBraced` predicate was added to the `Initializer` class which holds when a C++ braced initializer was used in the initialization.
+
## 0.2.2
### Deprecated APIs
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-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-05-30-braced-initializers.md b/cpp/ql/lib/change-notes/released/0.2.3.md
similarity index 83%
rename from cpp/ql/lib/change-notes/2022-05-30-braced-initializers.md
rename to cpp/ql/lib/change-notes/released/0.2.3.md
index 8a31f06ab98..87374099dc2 100644
--- a/cpp/ql/lib/change-notes/2022-05-30-braced-initializers.md
+++ b/cpp/ql/lib/change-notes/released/0.2.3.md
@@ -1,4 +1,5 @@
----
-category: feature
----
+## 0.2.3
+
+### New Features
+
* 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/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 16a06790aa8..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.2
+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 59dbe66d65c..06e68dba48c 100644
--- a/cpp/ql/lib/qlpack.yml
+++ b/cpp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-all
-version: 0.2.3-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/UserType.qll b/cpp/ql/lib/semmle/code/cpp/UserType.qll
index 13697722190..9d85f555ec1 100644
--- a/cpp/ql/lib/semmle/code/cpp/UserType.qll
+++ b/cpp/ql/lib/semmle/code/cpp/UserType.qll
@@ -48,8 +48,8 @@ class UserType extends Type, Declaration, NameQualifyingElement, AccessHolder, @
}
override TypeDeclarationEntry getADeclarationEntry() {
- if type_decls(_, underlyingElement(this), _)
- then type_decls(unresolveElement(result), underlyingElement(this), _)
+ if type_decls(_, unresolveElement(this), _)
+ then type_decls(underlyingElement(result), unresolveElement(this), _)
else exists(Class t | this.(Class).isConstructedFrom(t) and result = t.getADeclarationEntry())
}
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/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 e3602649581..340bfe280b7 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..340bfe280b7 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..340bfe280b7 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..340bfe280b7 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..340bfe280b7 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 af74d307cca..da8e7564b3e 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll
@@ -850,6 +850,34 @@ class ContentSet instanceof Content {
}
/**
+ * Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
+ *
+ * The expression `e` is expected to be a syntactic part of the guard `g`.
+ * For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
+ * the argument `x`.
+ */
+signature predicate guardChecksSig(GuardCondition g, Expr e, boolean branch);
+
+/**
+ * Provides a set of barrier nodes for a guard that validates an expression.
+ *
+ * This is expected to be used in `isBarrier`/`isSanitizer` definitions
+ * in data flow and taint tracking.
+ */
+module BarrierGuard {
+ /** Gets a node that is safely guarded by the given guard check. */
+ ExprNode getABarrierNode() {
+ exists(GuardCondition g, SsaDefinition def, Variable v, boolean branch |
+ result.getExpr() = def.getAUse(v) and
+ guardChecks(g, def.getAUse(v), branch) and
+ g.controls(result.getExpr().getBasicBlock(), branch)
+ )
+ }
+}
+
+/**
+ * DEPRECATED: Use `BarrierGuard` module instead.
+ *
* A guard that validates some expression.
*
* To use this in a configuration, extend the class and provide a
@@ -858,7 +886,7 @@ class ContentSet instanceof Content {
*
* It is important that all extending classes in scope are disjoint.
*/
-class BarrierGuard extends GuardCondition {
+deprecated class BarrierGuard extends GuardCondition {
/** Override this predicate to hold if this guard validates `e` upon evaluating to `b`. */
abstract predicate checks(Expr e, boolean b);
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
index 6fb13455286..6f19ad38d3d 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll
@@ -47,12 +47,6 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { n
*/
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
-/**
- * Holds if `guard` should be a sanitizer guard in all global taint flow configurations
- * but not in local taint.
- */
-predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
-
/**
* Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding
* local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
index 8cf5a49bc0b..e6ce1ada8d4 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
+ * Holds if taint propagation through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
- this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
+ deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ this.isSanitizerGuard(guard)
}
/**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ none()
+ }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ deprecated final override predicate isBarrierGuard(
+ DataFlow::BarrierGuard guard, DataFlow::FlowState state
+ ) {
this.isSanitizerGuard(guard, state)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
index 8cf5a49bc0b..e6ce1ada8d4 100644
--- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
+ * Holds if taint propagation through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
- this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
+ deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ this.isSanitizerGuard(guard)
}
/**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ none()
+ }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ deprecated final override predicate isBarrierGuard(
+ DataFlow::BarrierGuard guard, DataFlow::FlowState state
+ ) {
this.isSanitizerGuard(guard, state)
}
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 14399078231..68973293425 100644
--- a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
+++ b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll
@@ -49,6 +49,9 @@ class Expr extends StmtParent, @expr {
/** Gets the enclosing variable of this expression, if any. */
Variable getEnclosingVariable() { result = exprEnclosingElement(this) }
+ /** Gets the enclosing variable or function of this expression. */
+ Declaration getEnclosingDeclaration() { result = exprEnclosingElement(this) }
+
/** Gets a child of this expression. */
Expr getAChild() { exists(int n | result = this.getChild(n)) }
@@ -593,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/QualifiedName.qll b/cpp/ql/lib/semmle/code/cpp/internal/QualifiedName.qll
index 7cf0c647142..6d795048734 100644
--- a/cpp/ql/lib/semmle/code/cpp/internal/QualifiedName.qll
+++ b/cpp/ql/lib/semmle/code/cpp/internal/QualifiedName.qll
@@ -4,11 +4,7 @@
* qualified.
*
* This file contains classes that mirror the standard AST classes for C++, but
- * these classes are only concerned with naming. The other difference is that
- * these classes don't use the `ResolveClass.qll` mechanisms like
- * `unresolveElement` because these classes should eventually be part of the
- * implementation of `ResolveClass.qll`, allowing it to match up classes when
- * their qualified names and parameters match.
+ * these classes are only concerned with naming.
*/
private import semmle.code.cpp.Declaration as D
diff --git a/cpp/ql/lib/semmle/code/cpp/internal/ResolveClass.qll b/cpp/ql/lib/semmle/code/cpp/internal/ResolveClass.qll
index 42568a5c58d..e9882383dca 100644
--- a/cpp/ql/lib/semmle/code/cpp/internal/ResolveClass.qll
+++ b/cpp/ql/lib/semmle/code/cpp/internal/ResolveClass.qll
@@ -115,15 +115,13 @@ private module Cached {
*/
cached
predicate isClass(@usertype t) {
- (
- usertypes(t, _, 1) or
- usertypes(t, _, 2) or
- usertypes(t, _, 3) or
- usertypes(t, _, 6) or
- usertypes(t, _, 10) or
- usertypes(t, _, 11) or
- usertypes(t, _, 12)
- )
+ usertypes(t, _, 1) or
+ usertypes(t, _, 2) or
+ usertypes(t, _, 3) or
+ usertypes(t, _, 6) or
+ usertypes(t, _, 10) or
+ usertypes(t, _, 11) or
+ usertypes(t, _, 12)
}
cached
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 e3602649581..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
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..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
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..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
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..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
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
index 4171f5a5227..184472c91a7 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll
@@ -100,7 +100,7 @@ class Node extends TIRDataFlowNode {
Declaration getEnclosingCallable() { none() } // overridden in subclasses
/** Gets the function to which this node belongs, if any. */
- Function getFunction() { none() } // overridden in subclasses
+ Declaration getFunction() { none() } // overridden in subclasses
/** Gets the type of this node. */
IRType getType() { none() } // overridden in subclasses
@@ -196,7 +196,7 @@ class InstructionNode extends Node, TInstructionNode {
override Declaration getEnclosingCallable() { result = this.getFunction() }
- override Function getFunction() { result = instr.getEnclosingFunction() }
+ override Declaration getFunction() { result = instr.getEnclosingFunction() }
override IRType getType() { result = instr.getResultIRType() }
@@ -222,7 +222,7 @@ class OperandNode extends Node, TOperandNode {
override Declaration getEnclosingCallable() { result = this.getFunction() }
- override Function getFunction() { result = op.getUse().getEnclosingFunction() }
+ override Declaration getFunction() { result = op.getUse().getEnclosingFunction() }
override IRType getType() { result = op.getIRType() }
@@ -274,7 +274,7 @@ class StoreNodeInstr extends StoreNode, TStoreNodeInstr {
/** Gets the underlying instruction. */
Instruction getInstruction() { result = instr }
- override Function getFunction() { result = this.getInstruction().getEnclosingFunction() }
+ override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
override IRType getType() { result = this.getInstruction().getResultIRType() }
@@ -328,7 +328,7 @@ class StoreNodeOperand extends StoreNode, TStoreNodeOperand {
/** Gets the underlying operand. */
Operand getOperand() { result = operand }
- override Function getFunction() { result = operand.getDef().getEnclosingFunction() }
+ override Declaration getFunction() { result = operand.getDef().getEnclosingFunction() }
override IRType getType() { result = operand.getIRType() }
@@ -384,7 +384,7 @@ class ReadNode extends Node, TReadNode {
override Declaration getEnclosingCallable() { result = this.getFunction() }
- override Function getFunction() { result = this.getInstruction().getEnclosingFunction() }
+ override Declaration getFunction() { result = this.getInstruction().getEnclosingFunction() }
override IRType getType() { result = this.getInstruction().getResultIRType() }
@@ -436,7 +436,7 @@ class SsaPhiNode extends Node, TSsaPhiNode {
override Declaration getEnclosingCallable() { result = this.getFunction() }
- override Function getFunction() { result = phi.getBasicBlock().getEnclosingFunction() }
+ override Declaration getFunction() { result = phi.getBasicBlock().getEnclosingFunction() }
override IRType getType() { result instanceof IRVoidType }
@@ -673,7 +673,7 @@ class VariableNode extends Node, TVariableNode {
/** Gets the variable corresponding to this node. */
Variable getVariable() { result = v }
- override Function getFunction() { none() }
+ override Declaration getFunction() { none() }
override Declaration getEnclosingCallable() {
// When flow crosses from one _enclosing callable_ to another, the
@@ -1092,6 +1092,56 @@ class ContentSet instanceof Content {
}
/**
+ * Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
+ *
+ * The expression `e` is expected to be a syntactic part of the guard `g`.
+ * For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
+ * the argument `x`.
+ */
+signature predicate guardChecksSig(IRGuardCondition g, Expr e, boolean branch);
+
+/**
+ * Provides a set of barrier nodes for a guard that validates an expression.
+ *
+ * This is expected to be used in `isBarrier`/`isSanitizer` definitions
+ * in data flow and taint tracking.
+ */
+module BarrierGuard {
+ /** Gets a node that is safely guarded by the given guard check. */
+ ExprNode getABarrierNode() {
+ exists(IRGuardCondition g, ValueNumber value, boolean edge |
+ guardChecks(g, value.getAnInstruction().getConvertedResultExpression(), edge) and
+ result.asInstruction() = value.getAnInstruction() and
+ g.controls(result.asInstruction().getBlock(), edge)
+ )
+ }
+}
+
+/**
+ * Holds if the guard `g` validates the instruction `instr` upon evaluating to `branch`.
+ */
+signature predicate instructionGuardChecksSig(IRGuardCondition g, Instruction instr, boolean branch);
+
+/**
+ * Provides a set of barrier nodes for a guard that validates an instruction.
+ *
+ * This is expected to be used in `isBarrier`/`isSanitizer` definitions
+ * in data flow and taint tracking.
+ */
+module InstructionBarrierGuard {
+ /** Gets a node that is safely guarded by the given guard check. */
+ ExprNode getABarrierNode() {
+ exists(IRGuardCondition g, ValueNumber value, boolean edge |
+ instructionGuardChecks(g, value.getAnInstruction(), edge) and
+ result.asInstruction() = value.getAnInstruction() and
+ g.controls(result.asInstruction().getBlock(), edge)
+ )
+ }
+}
+
+/**
+ * DEPRECATED: Use `BarrierGuard` module instead.
+ *
* A guard that validates some instruction.
*
* To use this in a configuration, extend the class and provide a
@@ -1100,7 +1150,7 @@ class ContentSet instanceof Content {
*
* It is important that all extending classes in scope are disjoint.
*/
-class BarrierGuard extends IRGuardCondition {
+deprecated class BarrierGuard extends IRGuardCondition {
/** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */
predicate checksInstr(Instruction instr, boolean b) { none() }
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll
index 16182296e40..7359656e5a4 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll
@@ -94,12 +94,6 @@ private string getNodeProperty(DataFlow::Node node, string key) {
any(DataFlow::Configuration cfg).isBarrierIn(node) and kind = "in"
or
any(DataFlow::Configuration cfg).isBarrierOut(node) and kind = "out"
- or
- exists(DataFlow::BarrierGuard guard |
- any(DataFlow::Configuration cfg).isBarrierGuard(guard) and
- node = guard.getAGuardedNode() and
- kind = "guard(" + guard.getResultId() + ")"
- )
|
kind, ", "
)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
index 1086701a97f..4fe646a80af 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll
@@ -163,12 +163,6 @@ predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { n
*/
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
-/**
- * Holds if `guard` should be a sanitizer guard in all global taint flow configurations
- * but not in local taint.
- */
-predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
-
/**
* Holds if taint can flow from `instrIn` to `instrOut` through a call to a
* modeled function.
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
index 8cf5a49bc0b..e6ce1ada8d4 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
+ * Holds if taint propagation through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
- this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
+ deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ this.isSanitizerGuard(guard)
}
/**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ none()
+ }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ deprecated final override predicate isBarrierGuard(
+ DataFlow::BarrierGuard guard, DataFlow::FlowState state
+ ) {
this.isSanitizerGuard(guard, state)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
index 8cf5a49bc0b..e6ce1ada8d4 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
+ * Holds if taint propagation through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
- this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
+ deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ this.isSanitizerGuard(guard)
}
/**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ none()
+ }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ deprecated final override predicate isBarrierGuard(
+ DataFlow::BarrierGuard guard, DataFlow::FlowState state
+ ) {
this.isSanitizerGuard(guard, state)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
index 8cf5a49bc0b..e6ce1ada8d4 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
+ * Holds if taint propagation through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
- this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
+ deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ this.isSanitizerGuard(guard)
}
/**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ none()
+ }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ deprecated final override predicate isBarrierGuard(
+ DataFlow::BarrierGuard guard, DataFlow::FlowState state
+ ) {
this.isSanitizerGuard(guard, state)
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll
index 37ac2fccdd9..90cdb9e0f5f 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/IRConfiguration.qll
@@ -16,7 +16,7 @@ class IRConfiguration extends TIRConfiguration {
/**
* Holds if IR should be created for function `func`. By default, holds for all functions.
*/
- predicate shouldCreateIRForFunction(Language::Function func) { any() }
+ predicate shouldCreateIRForFunction(Language::Declaration func) { any() }
/**
* Holds if the strings used as part of an IR dump should be generated for function `func`.
@@ -25,7 +25,7 @@ class IRConfiguration extends TIRConfiguration {
* of debug strings for IR that will not be dumped. We still generate the actual IR for these
* functions, however, to preserve the results of any interprocedural analysis.
*/
- predicate shouldEvaluateDebugStringsForFunction(Language::Function func) { any() }
+ predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) { any() }
}
private newtype TIREscapeAnalysisConfiguration = MkIREscapeAnalysisConfiguration()
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll
index bac7634cbd0..78008a6c69b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRBlock.qll
@@ -97,7 +97,7 @@ class IRBlockBase extends TIRBlock {
/**
* Gets the `Function` that contains this block.
*/
- final Language::Function getEnclosingFunction() {
+ final Language::Declaration getEnclosingFunction() {
result = getFirstInstruction(this).getEnclosingFunction()
}
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll
index 31983d34247..45b44b14a3c 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRConsistency.qll
@@ -524,4 +524,23 @@ module InstructionConsistency {
"' has a `this` argument operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
+
+ query predicate nonUniqueIRVariable(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(VariableInstruction vi, IRVariable v1, IRVariable v2 |
+ instr = vi and vi.getIRVariable() = v1 and vi.getIRVariable() = v2 and v1 != v2
+ ) and
+ message =
+ "Variable instruction '" + instr.toString() +
+ "' has multiple associated variables, in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ or
+ instr.getOpcode() instanceof Opcode::VariableAddress and
+ not instr instanceof VariableInstruction and
+ message =
+ "Variable address instruction '" + instr.toString() +
+ "' has no associated variable, in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll
index ca4708857a7..c92082d767d 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/IRVariable.qll
@@ -18,7 +18,7 @@ private import Imports::IRType
* by the AST-to-IR translation (`IRTempVariable`).
*/
class IRVariable extends TIRVariable {
- Language::Function func;
+ Language::Declaration func;
IRVariable() {
this = TIRUserVariable(_, _, func) or
@@ -79,7 +79,7 @@ class IRVariable extends TIRVariable {
/**
* Gets the function that references this variable.
*/
- final Language::Function getEnclosingFunction() { result = func }
+ final Language::Declaration getEnclosingFunction() { result = func }
}
/**
@@ -246,7 +246,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter {
final override string toString() { result = "#ellipsis" }
- final override int getIndex() { result = func.getNumberOfParameters() }
+ final override int getIndex() { result = func.(Language::Function).getNumberOfParameters() }
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
index e5a908bbf9a..8e863ddf635 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/Instruction.qll
@@ -194,7 +194,7 @@ class Instruction extends Construction::TStageInstruction {
/**
* Gets the function that contains this instruction.
*/
- final Language::Function getEnclosingFunction() {
+ final Language::Declaration getEnclosingFunction() {
result = this.getEnclosingIRFunction().getFunction()
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll
index 59dadee7154..53cdc75512b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/PrintIR.qll
@@ -26,20 +26,20 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
* Holds if the IR for `func` should be printed. By default, holds for all
* functions.
*/
- predicate shouldPrintFunction(Language::Function func) { any() }
+ predicate shouldPrintFunction(Language::Declaration decl) { any() }
}
/**
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
*/
private class FilteredIRConfiguration extends IRConfiguration {
- override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
+ override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) {
shouldPrintFunction(func)
}
}
-private predicate shouldPrintFunction(Language::Function func) {
- exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
+private predicate shouldPrintFunction(Language::Declaration decl) {
+ exists(PrintIRConfiguration config | config.shouldPrintFunction(decl))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll
index 60895ce3d26..576b4f9adf1 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/IRFunctionBase.qll
@@ -5,23 +5,28 @@
private import IRFunctionBaseInternal
private newtype TIRFunction =
- MkIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) }
+ TFunctionIRFunction(Language::Function func) { IRConstruction::Raw::functionHasIR(func) } or
+ TVarInitIRFunction(Language::GlobalVariable var) { IRConstruction::Raw::varHasIRFunc(var) }
/**
* The IR for a function. This base class contains only the predicates that are the same between all
* phases of the IR. Each instantiation of `IRFunction` extends this class.
*/
class IRFunctionBase extends TIRFunction {
- Language::Function func;
+ Language::Declaration decl;
- IRFunctionBase() { this = MkIRFunction(func) }
+ IRFunctionBase() {
+ this = TFunctionIRFunction(decl)
+ or
+ this = TVarInitIRFunction(decl)
+ }
/** Gets a textual representation of this element. */
- final string toString() { result = "IR: " + func.toString() }
+ final string toString() { result = "IR: " + decl.toString() }
/** Gets the function whose IR is represented. */
- final Language::Function getFunction() { result = func }
+ final Language::Declaration getFunction() { result = decl }
/** Gets the location of the function. */
- final Language::Location getLocation() { result = func.getLocation() }
+ final Language::Location getLocation() { result = decl.getLocation() }
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll
index 12a0c6e7898..fe72263249f 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TIRVariable.qll
@@ -2,21 +2,21 @@ private import TIRVariableInternal
private import Imports::TempVariableTag
newtype TIRVariable =
- TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Function func) {
+ TIRUserVariable(Language::Variable var, Language::LanguageType type, Language::Declaration func) {
Construction::hasUserVariable(func, var, type)
} or
TIRTempVariable(
- Language::Function func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
+ Language::Declaration func, Language::AST ast, TempVariableTag tag, Language::LanguageType type
) {
Construction::hasTempVariable(func, ast, tag, type)
} or
TIRDynamicInitializationFlag(
- Language::Function func, Language::Variable var, Language::LanguageType type
+ Language::Declaration func, Language::Variable var, Language::LanguageType type
) {
Construction::hasDynamicInitializationFlag(func, var, type)
} or
TIRStringLiteral(
- Language::Function func, Language::AST ast, Language::LanguageType type,
+ Language::Declaration func, Language::AST ast, Language::LanguageType type,
Language::StringLiteral literal
) {
Construction::hasStringLiteral(func, ast, type, literal)
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
index bac7634cbd0..78008a6c69b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRBlock.qll
@@ -97,7 +97,7 @@ class IRBlockBase extends TIRBlock {
/**
* Gets the `Function` that contains this block.
*/
- final Language::Function getEnclosingFunction() {
+ final Language::Declaration getEnclosingFunction() {
result = getFirstInstruction(this).getEnclosingFunction()
}
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll
index 31983d34247..45b44b14a3c 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRConsistency.qll
@@ -524,4 +524,23 @@ module InstructionConsistency {
"' has a `this` argument operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
+
+ query predicate nonUniqueIRVariable(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(VariableInstruction vi, IRVariable v1, IRVariable v2 |
+ instr = vi and vi.getIRVariable() = v1 and vi.getIRVariable() = v2 and v1 != v2
+ ) and
+ message =
+ "Variable instruction '" + instr.toString() +
+ "' has multiple associated variables, in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ or
+ instr.getOpcode() instanceof Opcode::VariableAddress and
+ not instr instanceof VariableInstruction and
+ message =
+ "Variable address instruction '" + instr.toString() +
+ "' has no associated variable, in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll
index ca4708857a7..c92082d767d 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/IRVariable.qll
@@ -18,7 +18,7 @@ private import Imports::IRType
* by the AST-to-IR translation (`IRTempVariable`).
*/
class IRVariable extends TIRVariable {
- Language::Function func;
+ Language::Declaration func;
IRVariable() {
this = TIRUserVariable(_, _, func) or
@@ -79,7 +79,7 @@ class IRVariable extends TIRVariable {
/**
* Gets the function that references this variable.
*/
- final Language::Function getEnclosingFunction() { result = func }
+ final Language::Declaration getEnclosingFunction() { result = func }
}
/**
@@ -246,7 +246,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter {
final override string toString() { result = "#ellipsis" }
- final override int getIndex() { result = func.getNumberOfParameters() }
+ final override int getIndex() { result = func.(Language::Function).getNumberOfParameters() }
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll
index e5a908bbf9a..8e863ddf635 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/Instruction.qll
@@ -194,7 +194,7 @@ class Instruction extends Construction::TStageInstruction {
/**
* Gets the function that contains this instruction.
*/
- final Language::Function getEnclosingFunction() {
+ final Language::Declaration getEnclosingFunction() {
result = this.getEnclosingIRFunction().getFunction()
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll
index 59dadee7154..53cdc75512b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/PrintIR.qll
@@ -26,20 +26,20 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
* Holds if the IR for `func` should be printed. By default, holds for all
* functions.
*/
- predicate shouldPrintFunction(Language::Function func) { any() }
+ predicate shouldPrintFunction(Language::Declaration decl) { any() }
}
/**
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
*/
private class FilteredIRConfiguration extends IRConfiguration {
- override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
+ override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) {
shouldPrintFunction(func)
}
}
-private predicate shouldPrintFunction(Language::Function func) {
- exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
+private predicate shouldPrintFunction(Language::Declaration decl) {
+ exists(PrintIRConfiguration config | config.shouldPrintFunction(decl))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
index 94bfc53875f..44e9ecbfe5e 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/IRConstruction.qll
@@ -13,6 +13,7 @@ private import TranslatedElement
private import TranslatedExpr
private import TranslatedStmt
private import TranslatedFunction
+private import TranslatedGlobalVar
TranslatedElement getInstructionTranslatedElement(Instruction instruction) {
instruction = TRawInstruction(result, _)
@@ -35,29 +36,41 @@ module Raw {
cached
predicate functionHasIR(Function func) { exists(getTranslatedFunction(func)) }
+ cached
+ predicate varHasIRFunc(GlobalOrNamespaceVariable var) {
+ var.hasInitializer() and
+ (
+ not var.getType().isDeeplyConst()
+ or
+ var.getInitializer().getExpr() instanceof StringLiteral
+ )
+ }
+
cached
predicate hasInstruction(TranslatedElement element, InstructionTag tag) {
element.hasInstruction(_, tag, _)
}
cached
- predicate hasUserVariable(Function func, Variable var, CppType type) {
- getTranslatedFunction(func).hasUserVariable(var, type)
+ predicate hasUserVariable(Declaration decl, Variable var, CppType type) {
+ getTranslatedFunction(decl).hasUserVariable(var, type)
+ or
+ getTranslatedVarInit(decl).hasUserVariable(var, type)
}
cached
- predicate hasTempVariable(Function func, Locatable ast, TempVariableTag tag, CppType type) {
+ predicate hasTempVariable(Declaration decl, Locatable ast, TempVariableTag tag, CppType type) {
exists(TranslatedElement element |
element.getAst() = ast and
- func = element.getFunction() and
+ decl = element.getFunction() and
element.hasTempVariable(tag, type)
)
}
cached
- predicate hasStringLiteral(Function func, Locatable ast, CppType type, StringLiteral literal) {
+ predicate hasStringLiteral(Declaration decl, Locatable ast, CppType type, StringLiteral literal) {
literal = ast and
- literal.getEnclosingFunction() = func and
+ literal.getEnclosingDeclaration() = decl and
getTypeForPRValue(literal.getType()) = type
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll
index 66c601736af..f8960cd205d 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedCall.qll
@@ -180,7 +180,7 @@ abstract class TranslatedSideEffects extends TranslatedElement {
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
- final override Function getFunction() { result = getExpr().getEnclosingFunction() }
+ final override Declaration getFunction() { result = getExpr().getEnclosingDeclaration() }
final override TranslatedElement getChild(int i) {
result =
@@ -375,7 +375,7 @@ abstract class TranslatedSideEffect extends TranslatedElement {
kind instanceof GotoEdge
}
- final override Function getFunction() { result = getParent().getFunction() }
+ final override Declaration getFunction() { result = getParent().getFunction() }
final override Instruction getPrimaryInstructionForSideEffect(InstructionTag tag) {
tag = OnlyInstructionTag() and
@@ -436,13 +436,6 @@ abstract class TranslatedArgumentSideEffect extends TranslatedSideEffect {
result = index
}
- /**
- * Gets the `TranslatedFunction` containing this expression.
- */
- final TranslatedFunction getEnclosingFunction() {
- result = getTranslatedFunction(call.getEnclosingFunction())
- }
-
final override predicate sideEffectInstruction(Opcode opcode, CppType type) {
opcode = sideEffectOpcode and
(
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 d11d718e215..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
@@ -25,9 +25,9 @@ private Element getRealParent(Expr expr) {
result.(Destructor).getADestruction() = expr
}
-IRUserVariable getIRUserVariable(Function func, Variable var) {
+IRUserVariable getIRUserVariable(Declaration decl, Variable var) {
result.getVariable() = var and
- result.getEnclosingFunction() = func
+ result.getEnclosingFunction() = decl
}
IRTempVariable getIRTempVariable(Locatable ast, TempVariableTag tag) {
@@ -67,7 +67,8 @@ private predicate ignoreExprAndDescendants(Expr expr) {
exists(Initializer init, StaticStorageDurationVariable var |
init = var.getInitializer() and
not var.hasDynamicInitialization() and
- expr = init.getExpr().getFullyConverted()
+ expr = init.getExpr().getFullyConverted() and
+ not var instanceof GlobalOrNamespaceVariable
)
or
// Ignore descendants of `__assume` expressions, since we translated these to `NoOp`.
@@ -117,7 +118,8 @@ private predicate ignoreExprOnly(Expr expr) {
// should not be translated.
exists(NewOrNewArrayExpr new | expr = new.getAllocatorCall().getArgument(0))
or
- not translateFunction(expr.getEnclosingFunction())
+ not translateFunction(expr.getEnclosingFunction()) and
+ not Raw::varHasIRFunc(expr.getEnclosingVariable())
or
// We do not yet translate destructors properly, so for now we ignore the
// destructor call. We do, however, translate the expression being
@@ -662,7 +664,8 @@ newtype TTranslatedElement =
opcode = getASideEffectOpcode(call, -1)
} or
// The side effect that initializes newly-allocated memory.
- TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) }
+ TTranslatedAllocationSideEffect(AllocationExpr expr) { not ignoreSideEffects(expr) } or
+ TTranslatedGlobalOrNamespaceVarInit(GlobalOrNamespaceVariable var) { Raw::varHasIRFunc(var) }
/**
* Gets the index of the first explicitly initialized element in `initList`
@@ -792,7 +795,7 @@ abstract class TranslatedElement extends TTranslatedElement {
/**
* Gets the `Function` that contains this element.
*/
- abstract Function getFunction();
+ abstract Declaration getFunction();
/**
* Gets the successor instruction of the instruction that was generated by
@@ -942,3 +945,14 @@ abstract class TranslatedElement extends TTranslatedElement {
*/
final TranslatedElement getParent() { result.getAChild() = this }
}
+
+/**
+ * The IR translation of a root element, either a function or a global variable.
+ */
+abstract class TranslatedRootElement extends TranslatedElement {
+ TranslatedRootElement() {
+ this instanceof TTranslatedFunction
+ or
+ this instanceof TTranslatedGlobalOrNamespaceVarInit
+ }
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
index c81b7c5943a..f91486833ff 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedExpr.qll
@@ -12,6 +12,7 @@ private import TranslatedElement
private import TranslatedFunction
private import TranslatedInitialization
private import TranslatedStmt
+private import TranslatedGlobalVar
import TranslatedCall
/**
@@ -78,7 +79,7 @@ abstract class TranslatedExpr extends TranslatedElement {
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = this.getAst() }
- final override Function getFunction() { result = expr.getEnclosingFunction() }
+ final override Declaration getFunction() { result = expr.getEnclosingDeclaration() }
/**
* Gets the expression from which this `TranslatedExpr` is generated.
@@ -88,8 +89,10 @@ abstract class TranslatedExpr extends TranslatedElement {
/**
* Gets the `TranslatedFunction` containing this expression.
*/
- final TranslatedFunction getEnclosingFunction() {
+ final TranslatedRootElement getEnclosingFunction() {
result = getTranslatedFunction(expr.getEnclosingFunction())
+ or
+ result = getTranslatedVarInit(expr.getEnclosingVariable())
}
}
@@ -787,7 +790,7 @@ class TranslatedThisExpr extends TranslatedNonConstantExpr {
override IRVariable getInstructionVariable(InstructionTag tag) {
tag = ThisAddressTag() and
- result = this.getEnclosingFunction().getThisVariable()
+ result = this.getEnclosingFunction().(TranslatedFunction).getThisVariable()
}
}
@@ -838,7 +841,7 @@ class TranslatedNonFieldVariableAccess extends TranslatedVariableAccess {
override IRVariable getInstructionVariable(InstructionTag tag) {
tag = OnlyInstructionTag() and
- result = getIRUserVariable(expr.getEnclosingFunction(), expr.getTarget())
+ result = getIRUserVariable(expr.getEnclosingDeclaration(), expr.getTarget())
}
}
@@ -2522,7 +2525,7 @@ class TranslatedVarArgsStart extends TranslatedNonConstantExpr {
final override IRVariable getInstructionVariable(InstructionTag tag) {
tag = VarArgsStartEllipsisAddressTag() and
- result = this.getEnclosingFunction().getEllipsisVariable()
+ result = this.getEnclosingFunction().(TranslatedFunction).getEllipsisVariable()
}
final override Instruction getInstructionRegisterOperand(InstructionTag tag, OperandTag operandTag) {
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
index 0f781cb2244..b4746ae58de 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedFunction.qll
@@ -58,7 +58,7 @@ predicate hasReturnValue(Function func) { not func.getUnspecifiedType() instance
* Represents the IR translation of a function. This is the root elements for
* all other elements associated with this function.
*/
-class TranslatedFunction extends TranslatedElement, TTranslatedFunction {
+class TranslatedFunction extends TranslatedRootElement, TTranslatedFunction {
Function func;
TranslatedFunction() { this = TTranslatedFunction(func) }
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
new file mode 100644
index 00000000000..dde5e00361a
--- /dev/null
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll
@@ -0,0 +1,132 @@
+import semmle.code.cpp.ir.implementation.raw.internal.TranslatedElement
+private import cpp
+private import semmle.code.cpp.ir.implementation.IRType
+private import semmle.code.cpp.ir.implementation.Opcode
+private import semmle.code.cpp.ir.implementation.internal.OperandTag
+private import semmle.code.cpp.ir.internal.CppType
+private import TranslatedInitialization
+private import InstructionTag
+private import semmle.code.cpp.ir.internal.IRUtilities
+
+class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement,
+ TTranslatedGlobalOrNamespaceVarInit, InitializationContext {
+ GlobalOrNamespaceVariable var;
+
+ TranslatedGlobalOrNamespaceVarInit() { this = TTranslatedGlobalOrNamespaceVarInit(var) }
+
+ override string toString() { result = var.toString() }
+
+ final override GlobalOrNamespaceVariable getAst() { result = var }
+
+ final override Declaration getFunction() { result = var }
+
+ final Location getLocation() { result = var.getLocation() }
+
+ override Instruction getFirstInstruction() { result = this.getInstruction(EnterFunctionTag()) }
+
+ override TranslatedElement getChild(int n) {
+ n = 1 and
+ result = getTranslatedInitialization(var.getInitializer().getExpr().getFullyConverted())
+ }
+
+ override predicate hasInstruction(Opcode op, InstructionTag tag, CppType type) {
+ op instanceof Opcode::EnterFunction and
+ tag = EnterFunctionTag() and
+ type = getVoidType()
+ or
+ op instanceof Opcode::AliasedDefinition and
+ tag = AliasedDefinitionTag() and
+ type = getUnknownType()
+ or
+ op instanceof Opcode::VariableAddress and
+ tag = InitializerVariableAddressTag() and
+ type = getTypeForGLValue(var.getType())
+ or
+ op instanceof Opcode::ReturnVoid and
+ tag = ReturnTag() and
+ type = getVoidType()
+ or
+ op instanceof Opcode::AliasedUse and
+ tag = AliasedUseTag() and
+ type = getVoidType()
+ or
+ op instanceof Opcode::ExitFunction and
+ tag = ExitFunctionTag() and
+ type = getVoidType()
+ }
+
+ override Instruction getInstructionSuccessor(InstructionTag tag, EdgeKind kind) {
+ kind instanceof GotoEdge and
+ (
+ tag = EnterFunctionTag() and
+ result = this.getInstruction(AliasedDefinitionTag())
+ or
+ tag = AliasedDefinitionTag() and
+ result = this.getInstruction(InitializerVariableAddressTag())
+ or
+ tag = InitializerVariableAddressTag() and
+ result = this.getChild(1).getFirstInstruction()
+ or
+ tag = ReturnTag() and
+ result = this.getInstruction(AliasedUseTag())
+ or
+ tag = AliasedUseTag() and
+ result = this.getInstruction(ExitFunctionTag())
+ )
+ }
+
+ override Instruction getChildSuccessor(TranslatedElement child) {
+ child = this.getChild(1) and
+ result = this.getInstruction(ReturnTag())
+ }
+
+ final override CppType getInstructionMemoryOperandType(
+ InstructionTag tag, TypedOperandTag operandTag
+ ) {
+ tag = AliasedUseTag() and
+ operandTag instanceof SideEffectOperandTag and
+ result = getUnknownType()
+ }
+
+ override IRUserVariable getInstructionVariable(InstructionTag tag) {
+ tag = InitializerVariableAddressTag() and
+ result.getVariable() = var and
+ result.getEnclosingFunction() = var
+ }
+
+ override Instruction getTargetAddress() {
+ result = this.getInstruction(InitializerVariableAddressTag())
+ }
+
+ override Type getTargetType() { result = var.getUnspecifiedType() }
+
+ /**
+ * Holds if this variable defines or accesses variable `var` with type `type`. This includes all
+ * parameters and local variables, plus any global variables or static data members that are
+ * directly accessed by the function.
+ */
+ final predicate hasUserVariable(Variable varUsed, CppType type) {
+ (
+ (
+ varUsed instanceof GlobalOrNamespaceVariable
+ or
+ varUsed instanceof MemberVariable and not varUsed instanceof Field
+ ) and
+ exists(VariableAccess access |
+ access.getTarget() = varUsed and
+ access.getEnclosingVariable() = var
+ )
+ or
+ var = varUsed
+ or
+ varUsed.(LocalScopeVariable).getEnclosingElement*() = var
+ or
+ varUsed.(Parameter).getCatchBlock().getEnclosingElement*() = var
+ ) and
+ type = getTypeForPRValue(getVariableType(varUsed))
+ }
+}
+
+TranslatedGlobalOrNamespaceVarInit getTranslatedVarInit(GlobalOrNamespaceVariable var) {
+ result.getAst() = var
+}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
index 1a9d7ad9d70..b800405a73b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedInitialization.qll
@@ -137,7 +137,10 @@ abstract class TranslatedInitialization extends TranslatedElement, TTranslatedIn
final override string toString() { result = "init: " + expr.toString() }
- final override Function getFunction() { result = expr.getEnclosingFunction() }
+ final override Declaration getFunction() {
+ result = expr.getEnclosingFunction() or
+ result = expr.getEnclosingVariable().(GlobalOrNamespaceVariable)
+ }
final override Locatable getAst() { result = expr }
@@ -486,7 +489,10 @@ abstract class TranslatedFieldInitialization extends TranslatedElement {
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
- final override Function getFunction() { result = ast.getEnclosingFunction() }
+ final override Declaration getFunction() {
+ result = ast.getEnclosingFunction() or
+ result = ast.getEnclosingVariable().(GlobalOrNamespaceVariable)
+ }
final override Instruction getFirstInstruction() { result = getInstruction(getFieldAddressTag()) }
@@ -633,7 +639,11 @@ abstract class TranslatedElementInitialization extends TranslatedElement {
/** DEPRECATED: Alias for getAst */
deprecated override Locatable getAST() { result = getAst() }
- final override Function getFunction() { result = initList.getEnclosingFunction() }
+ final override Declaration getFunction() {
+ result = initList.getEnclosingFunction()
+ or
+ result = initList.getEnclosingVariable().(GlobalOrNamespaceVariable)
+ }
final override Instruction getFirstInstruction() { result = getInstruction(getElementIndexTag()) }
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll
index bac7634cbd0..78008a6c69b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRBlock.qll
@@ -97,7 +97,7 @@ class IRBlockBase extends TIRBlock {
/**
* Gets the `Function` that contains this block.
*/
- final Language::Function getEnclosingFunction() {
+ final Language::Declaration getEnclosingFunction() {
result = getFirstInstruction(this).getEnclosingFunction()
}
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll
index 31983d34247..45b44b14a3c 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRConsistency.qll
@@ -524,4 +524,23 @@ module InstructionConsistency {
"' has a `this` argument operand that is not an address, in function '$@'." and
irFunc = getInstructionIRFunction(instr, irFuncText)
}
+
+ query predicate nonUniqueIRVariable(
+ Instruction instr, string message, OptionalIRFunction irFunc, string irFuncText
+ ) {
+ exists(VariableInstruction vi, IRVariable v1, IRVariable v2 |
+ instr = vi and vi.getIRVariable() = v1 and vi.getIRVariable() = v2 and v1 != v2
+ ) and
+ message =
+ "Variable instruction '" + instr.toString() +
+ "' has multiple associated variables, in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ or
+ instr.getOpcode() instanceof Opcode::VariableAddress and
+ not instr instanceof VariableInstruction and
+ message =
+ "Variable address instruction '" + instr.toString() +
+ "' has no associated variable, in function '$@'." and
+ irFunc = getInstructionIRFunction(instr, irFuncText)
+ }
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll
index ca4708857a7..c92082d767d 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/IRVariable.qll
@@ -18,7 +18,7 @@ private import Imports::IRType
* by the AST-to-IR translation (`IRTempVariable`).
*/
class IRVariable extends TIRVariable {
- Language::Function func;
+ Language::Declaration func;
IRVariable() {
this = TIRUserVariable(_, _, func) or
@@ -79,7 +79,7 @@ class IRVariable extends TIRVariable {
/**
* Gets the function that references this variable.
*/
- final Language::Function getEnclosingFunction() { result = func }
+ final Language::Declaration getEnclosingFunction() { result = func }
}
/**
@@ -246,7 +246,7 @@ class IREllipsisVariable extends IRTempVariable, IRParameter {
final override string toString() { result = "#ellipsis" }
- final override int getIndex() { result = func.getNumberOfParameters() }
+ final override int getIndex() { result = func.(Language::Function).getNumberOfParameters() }
}
/**
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
index e5a908bbf9a..8e863ddf635 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/Instruction.qll
@@ -194,7 +194,7 @@ class Instruction extends Construction::TStageInstruction {
/**
* Gets the function that contains this instruction.
*/
- final Language::Function getEnclosingFunction() {
+ final Language::Declaration getEnclosingFunction() {
result = this.getEnclosingIRFunction().getFunction()
}
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll
index 59dadee7154..53cdc75512b 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/PrintIR.qll
@@ -26,20 +26,20 @@ class PrintIRConfiguration extends TPrintIRConfiguration {
* Holds if the IR for `func` should be printed. By default, holds for all
* functions.
*/
- predicate shouldPrintFunction(Language::Function func) { any() }
+ predicate shouldPrintFunction(Language::Declaration decl) { any() }
}
/**
* Override of `IRConfiguration` to only evaluate debug strings for the functions that are to be dumped.
*/
private class FilteredIRConfiguration extends IRConfiguration {
- override predicate shouldEvaluateDebugStringsForFunction(Language::Function func) {
+ override predicate shouldEvaluateDebugStringsForFunction(Language::Declaration func) {
shouldPrintFunction(func)
}
}
-private predicate shouldPrintFunction(Language::Function func) {
- exists(PrintIRConfiguration config | config.shouldPrintFunction(func))
+private predicate shouldPrintFunction(Language::Declaration decl) {
+ exists(PrintIRConfiguration config | config.shouldPrintFunction(decl))
}
private string getAdditionalInstructionProperty(Instruction instr, string key) {
diff --git a/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll b/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll
index f047d6c4753..46e3e6dec1c 100644
--- a/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll
+++ b/cpp/ql/lib/semmle/code/cpp/ir/internal/IRCppLanguage.qll
@@ -50,12 +50,16 @@ class AutomaticVariable = Cpp::StackVariable;
class StaticVariable = Cpp::Variable;
+class GlobalVariable = Cpp::GlobalOrNamespaceVariable;
+
class Parameter = Cpp::Parameter;
class Field = Cpp::Field;
class BuiltInOperation = Cpp::BuiltInOperation;
+class Declaration = Cpp::Declaration;
+
// TODO: Remove necessity for these.
class Expr = Cpp::Expr;
diff --git a/cpp/ql/lib/semmlecode.cpp.dbscheme b/cpp/ql/lib/semmlecode.cpp.dbscheme
index 19e31bf071f..23f7cbb88a4 100644
--- a/cpp/ql/lib/semmlecode.cpp.dbscheme
+++ b/cpp/ql/lib/semmlecode.cpp.dbscheme
@@ -1650,6 +1650,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 +1716,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..35eb9c7e6de 100644
--- a/cpp/ql/lib/semmlecode.cpp.dbscheme.stats
+++ b/cpp/ql/lib/semmlecode.cpp.dbscheme.stats
@@ -2,79 +2,79 @@
@compilation
- 9980
+ 9978
@externalDataElement
65
-
- @svnentry
- 575525
-
@external_package
4
+
+ @svnentry
+ 575525
+
@location_stmt
- 3805462
+ 3805465
@diagnostic
- 582773
+ 72312
@file
- 124189
+ 124196
@folder
15994
- @location_expr
- 13128112
+ @location_default
+ 30132211
- @location_default
- 30128761
+ @location_expr
+ 13128120
@macroinvocation
- 33894981
+ 33889080
@function
- 4726273
+ 4726519
@fun_decl
- 5096962
+ 5097227
@type_decl
- 3283977
+ 3284148
@namespace_decl
- 306972
+ 306973
@using
- 374921
+ 374941
@static_assert
- 130417
+ 130418
@var_decl
- 8543232
+ 8543677
@parameter
- 6660155
+ 6660502
@membervariable
@@ -82,7 +82,7 @@
@globalvariable
- 300708
+ 318724
@localvariable
@@ -94,55 +94,55 @@
@builtintype
- 22109
+ 22110
@derivedtype
- 4413446
+ 4413676
@decltype
- 31047
+ 31049
@usertype
- 5342989
+ 5343268
@mangledname
- 1728010
+ 1975901
@type_mention
- 4011508
+ 4011511
@routinetype
- 546982
+ 546661
@ptrtomember
- 38103
+ 38105
@specifier
- 24932
+ 24933
@gnuattribute
- 664228
+ 664262
@stdattribute
- 468982
+ 469081
@alignas
- 8937
+ 8938
@declspec
- 238544
+ 238545
@msattribute
@@ -150,11 +150,11 @@
@attribute_arg_token
- 25402
+ 25403
@attribute_arg_constant
- 326469
+ 326486
@attribute_arg_type
@@ -166,15 +166,15 @@
@derivation
- 402388
+ 402152
@frienddecl
- 715075
+ 714656
@comment
- 8781270
+ 8783134
@namespace
@@ -186,23 +186,23 @@
@namequalifier
- 1618961
+ 1618866
@value
- 10646146
+ 10646153
@initialiser
- 1731824
+ 1732353
@lambdacapture
- 28224
+ 28226
@stmt_expr
- 1480414
+ 1480415
@stmt_if
@@ -210,23 +210,23 @@
@stmt_while
- 30207
+ 30201
@stmt_label
- 53046
+ 53044
@stmt_return
- 1306346
+ 1306414
@stmt_block
- 1446530
+ 1446605
@stmt_end_test_while
- 148604
+ 148599
@stmt_for
@@ -234,7 +234,7 @@
@stmt_switch_case
- 191408
+ 191399
@stmt_switch
@@ -242,11 +242,11 @@
@stmt_try_block
- 42701
+ 42699
@stmt_decl
- 608508
+ 608387
@stmt_empty
@@ -258,7 +258,7 @@
@stmt_break
- 102231
+ 102232
@stmt_range_based_for
@@ -266,15 +266,15 @@
@stmt_handler
- 59432
+ 59429
@stmt_constexpr_if
- 52551
+ 52562
@stmt_goto
- 110490
+ 110487
@stmt_asm
@@ -310,43 +310,43 @@
@ctordirectinit
- 112813
+ 112747
@ctorvirtualinit
- 6534
+ 6532
@ctorfieldinit
- 200789
+ 200671
@ctordelegatinginit
- 3347
+ 3345
@dtordirectdestruct
- 41715
+ 41690
@dtorvirtualdestruct
- 4122
+ 4119
@dtorfielddestruct
- 41644
+ 41620
@static_cast
- 211821
+ 211675
@reinterpret_cast
- 30776
+ 30783
@const_cast
- 35278
+ 35285
@dynamic_cast
@@ -354,19 +354,19 @@
@c_style_cast
- 4209393
+ 4209396
@lambdaexpr
- 21639
+ 21640
@param_ref
- 375289
+ 375271
@errorexpr
- 46823
+ 46796
@address_of
@@ -374,27 +374,27 @@
@reference_to
- 1596143
+ 1596067
@indirect
- 292115
+ 292106
@ref_indirect
- 1934537
+ 1938685
@array_to_pointer
- 1424883
+ 1424884
@vacuous_destructor_call
- 8138
+ 8133
@parexpr
- 3572547
+ 3572549
@arithnegexpr
@@ -402,11 +402,11 @@
@complementexpr
- 27787
+ 27786
@notexpr
- 277992
+ 277993
@postincrexpr
@@ -446,11 +446,11 @@
@remexpr
- 15819
+ 15810
@paddexpr
- 86505
+ 86502
@psubexpr
@@ -458,7 +458,7 @@
@pdiffexpr
- 35487
+ 35495
@lshiftexpr
@@ -490,11 +490,11 @@
@gtexpr
- 100669
+ 100674
@ltexpr
- 106314
+ 106319
@geexpr
@@ -502,11 +502,11 @@
@leexpr
- 212141
+ 212134
@assignexpr
- 933419
+ 933420
@assignaddexpr
@@ -518,7 +518,7 @@
@assignmulexpr
- 7170
+ 7168
@assigndivexpr
@@ -546,7 +546,7 @@
@assignxorexpr
- 21804
+ 21803
@assignpaddexpr
@@ -554,7 +554,7 @@
@andlogicalexpr
- 249008
+ 249009
@orlogicalexpr
@@ -562,7 +562,7 @@
@commaexpr
- 181539
+ 181530
@subscriptexpr
@@ -570,7 +570,7 @@
@callexpr
- 309063
+ 309079
@vastartexpr
@@ -586,27 +586,27 @@
@varaccess
- 6006364
+ 6006368
@thisaccess
- 1125914
+ 1125919
@new_expr
- 47598
+ 47571
@delete_expr
- 11732
+ 11725
@throw_expr
- 21765
+ 21760
@condition_decl
- 38595
+ 38593
@braced_init_list
@@ -614,7 +614,7 @@
@type_id
- 36430
+ 36408
@runtime_sizeof
@@ -622,15 +622,15 @@
@runtime_alignof
- 48550
+ 48521
@sizeof_pack
- 5644
+ 5645
@routineexpr
- 3047613
+ 3047738
@type_operand
@@ -642,7 +642,7 @@
@ispodexpr
- 636
+ 635
@hastrivialdestructor
@@ -666,23 +666,23 @@
@isconstructibleexpr
- 2721
+ 2722
@isfinalexpr
- 2093
+ 2094
@noexceptexpr
- 23574
+ 23573
@builtinaddressof
- 13282
+ 13274
@temp_init
- 760683
+ 760646
@assume
@@ -826,7 +826,7 @@
@typescompexpr
- 562415
+ 562416
@intaddrexpr
@@ -846,7 +846,7 @@
@istriviallyconstructibleexpr
- 2826
+ 2827
@isdestructibleexpr
@@ -866,7 +866,7 @@
@isnothrowassignableexpr
- 2093
+ 2094
@isstandardlayoutexpr
@@ -890,7 +890,7 @@
@isnothrowconstructibleexpr
- 4815
+ 4816
@hasfinalizerexpr
@@ -956,45 +956,65 @@
@co_yield
1
+
+ @isassignable
+ 2617
+
+
+ @isaggregate
+ 2
+
+
+ @hasuniqueobjectrepresentations
+ 2
+
+
+ @builtinbitcast
+ 1
+
+
+ @builtinshuffle
+ 1965
+
@ppd_if
- 672225
+ 672260
@ppd_ifdef
- 265314
+ 265328
@ppd_ifndef
- 268607
+ 268621
@ppd_elif
- 25402
+ 25403
@ppd_else
- 210746
+ 210757
@ppd_endif
- 1206147
+ 1206210
@ppd_plain_include
- 313767
+ 313784
@ppd_define
- 2435252
+ 2435769
@ppd_undef
- 262021
+ 262035
@ppd_pragma
- 312270
+ 312337
@ppd_include_next
@@ -1048,11 +1068,11 @@
compilations
- 9980
+ 9978
id
- 9980
+ 9978
cwd
@@ -1070,7 +1090,7 @@
1
2
- 9980
+ 9978
@@ -1644,7 +1664,7 @@
seconds
- 12490
+ 9586
@@ -1725,48 +1745,48 @@
3
4
- 198
+ 715
4
5
- 795
+ 278
- 5
- 9
+ 6
+ 8
159
- 9
+ 8
+ 10
+ 159
+
+
+ 10
11
- 119
+ 79
11
12
- 198
-
-
- 13
- 18
159
- 18
- 22
+ 17
+ 19
+ 159
+
+
+ 20
+ 27
+ 159
+
+
+ 40
+ 77
119
-
- 22
- 46
- 159
-
-
- 60
- 104
- 79
-
@@ -1833,12 +1853,12 @@
3
4
- 556
+ 835
4
5
- 1233
+ 914
5
@@ -1848,32 +1868,27 @@
6
7
- 119
+ 437
7
8
- 318
+ 79
8
9
- 119
+ 278
9
- 11
+ 26
278
- 11
- 31
- 278
-
-
- 40
- 95
- 159
+ 26
+ 92
+ 238
@@ -1919,18 +1934,23 @@
12
- 4
- 5
- 79
-
-
- 152
- 153
+ 3
+ 4
39
- 160
- 161
+ 4
+ 5
+ 39
+
+
+ 112
+ 113
+ 39
+
+
+ 129
+ 130
39
@@ -1947,22 +1967,27 @@
1
2
- 7875
+ 5449
2
3
- 2704
+ 1670
3
4
- 1312
+ 1272
4
- 45
- 596
+ 5
+ 676
+
+
+ 5
+ 42
+ 517
@@ -1978,27 +2003,32 @@
1
2
- 6682
+ 5091
2
3
- 3142
+ 1392
3
4
- 1431
-
-
- 4
- 6
954
- 6
- 65
- 278
+ 4
+ 5
+ 875
+
+
+ 5
+ 7
+ 795
+
+
+ 7
+ 67
+ 477
@@ -2014,12 +2044,12 @@
1
2
- 12251
+ 9307
2
3
- 238
+ 278
@@ -2029,23 +2059,23 @@
diagnostic_for
- 1031027
+ 891808
diagnostic
- 582773
+ 72312
compilation
- 1988
+ 9585
file_number
- 104
+ 11
file_number_diagnostic_number
- 104788
+ 6856
@@ -2059,27 +2089,17 @@
1
2
- 362937
+ 9631
2
3
- 92540
+ 59836
- 3
- 4
- 70033
-
-
- 4
- 6
- 47421
-
-
- 6
- 9
- 9840
+ 254
+ 825
+ 2844
@@ -2095,7 +2115,7 @@
1
2
- 582773
+ 72312
@@ -2111,22 +2131,7 @@
1
2
- 475263
-
-
- 2
- 3
- 50143
-
-
- 3
- 6
- 53702
-
-
- 6
- 7
- 3663
+ 72312
@@ -2135,222 +2140,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 +2147,193 @@
2
3
- 25647
+ 57
7
8
- 10991
+ 6093
8
9
- 13085
+ 497
- 9
- 10
- 13608
+ 247
+ 248
+ 1965
- 10
- 11
- 27741
+ 263
+ 444
+ 763
- 11
- 12
- 5652
+ 446
+ 594
+ 208
+
+
+
+
+
+
+ compilation
+ file_number
+
+
+ 12
+
+
+ 1
+ 2
+ 9585
+
+
+
+
+
+
+ compilation
+ file_number_diagnostic_number
+
+
+ 12
+
+
+ 2
+ 3
+ 57
- 12
- 15
- 8060
+ 7
+ 8
+ 6093
+
+
+ 8
+ 9
+ 497
+
+
+ 247
+ 248
+ 1965
+
+
+ 263
+ 444
+ 763
+
+
+ 446
+ 594
+ 208
+
+
+
+
+
+
+ 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
+ 2821
+
+
+ 2
+ 5
+ 601
+
+
+ 5
+ 6
+ 1017
+
+
+ 7
+ 14
+ 543
+
+
+ 15
+ 16
+ 57
+
+
+ 17
+ 18
+ 601
+
+
+ 18
+ 23
+ 462
+
+
+ 26
+ 40
+ 555
+
+
+ 42
+ 830
+ 196
@@ -2402,49 +2347,54 @@
12
- 2
- 3
- 25647
-
-
- 8
+ 4
9
- 12247
-
-
- 9
- 10
- 7118
+ 589
10
11
- 6490
-
-
- 11
- 13
- 9526
-
-
- 13
- 14
- 6176
+ 1005
14
- 15
- 21355
+ 27
+ 543
- 15
- 16
- 8060
+ 30
+ 31
+ 57
- 16
- 20
- 8165
+ 34
+ 35
+ 601
+
+
+ 36
+ 45
+ 462
+
+
+ 52
+ 79
+ 555
+
+
+ 84
+ 85
+ 185
+
+
+ 254
+ 255
+ 2763
+
+
+ 297
+ 830
+ 92
@@ -2460,7 +2410,7 @@
1
2
- 104788
+ 6856
@@ -2470,19 +2420,19 @@
compilation_finished
- 9980
+ 9978
id
- 9980
+ 9978
cpu_seconds
- 7956
+ 7850
elapsed_seconds
- 138
+ 161
@@ -2496,7 +2446,7 @@
1
2
- 9980
+ 9978
@@ -2512,7 +2462,7 @@
1
2
- 9980
+ 9978
@@ -2528,17 +2478,17 @@
1
2
- 6777
+ 6498
2
3
- 786
+ 1005
3
- 17
- 393
+ 15
+ 346
@@ -2554,12 +2504,12 @@
1
2
- 7505
+ 7423
2
3
- 451
+ 427
@@ -2572,10 +2522,15 @@
12
+
+ 1
+ 2
+ 23
+
2
3
- 23
+ 11
3
@@ -2583,43 +2538,43 @@
23
- 9
- 10
+ 8
+ 9
+ 23
+
+
+ 11
+ 12
11
- 14
- 15
+ 24
+ 25
11
- 22
- 23
+ 49
+ 50
11
- 37
- 38
+ 104
+ 105
11
- 144
- 145
+ 155
+ 156
11
- 162
- 163
+ 239
+ 240
11
- 222
- 223
- 11
-
-
- 243
- 244
+ 255
+ 256
11
@@ -2633,10 +2588,15 @@
12
+
+ 1
+ 2
+ 23
+
2
3
- 23
+ 11
3
@@ -2644,43 +2604,43 @@
23
- 9
- 10
+ 8
+ 9
+ 23
+
+
+ 11
+ 12
11
- 14
- 15
+ 24
+ 25
11
- 22
- 23
+ 49
+ 50
11
- 36
- 37
+ 104
+ 105
11
- 118
- 119
+ 111
+ 112
11
- 123
- 124
+ 174
+ 175
11
- 177
- 178
- 11
-
-
- 218
- 219
+ 217
+ 218
11
@@ -4405,31 +4365,31 @@
locations_default
- 30128761
+ 30132211
id
- 30128761
+ 30132211
container
- 140184
+ 140191
startLine
- 2114992
+ 2115102
startColumn
- 37162
+ 37164
endLine
- 2117814
+ 2117925
endColumn
- 48452
+ 48455
@@ -4443,7 +4403,7 @@
1
2
- 30128761
+ 30132211
@@ -4459,7 +4419,7 @@
1
2
- 30128761
+ 30132211
@@ -4475,7 +4435,7 @@
1
2
- 30128761
+ 30132211
@@ -4491,7 +4451,7 @@
1
2
- 30128761
+ 30132211
@@ -4507,7 +4467,7 @@
1
2
- 30128761
+ 30132211
@@ -4523,67 +4483,67 @@
1
2
- 16464
+ 16465
2
12
- 10819
+ 10820
13
20
- 11760
+ 11761
21
36
- 11289
+ 11290
36
55
- 11289
+ 11290
55
77
- 10819
+ 10820
77
102
- 10819
+ 10820
102
149
- 10819
+ 10820
149
227
- 11289
+ 11290
228
350
- 11289
+ 10820
- 358
- 628
- 10819
+ 351
+ 604
+ 10820
- 671
- 1926
- 10819
+ 627
+ 1479
+ 10820
- 2168
+ 1925
2380
- 1881
+ 2352
@@ -4599,62 +4559,62 @@
1
2
- 16464
+ 16465
2
9
- 10819
+ 10820
9
16
- 11760
+ 11761
16
25
- 11289
+ 11290
25
40
- 10819
+ 10820
40
57
- 10819
+ 10820
58
72
- 10819
+ 10820
73
103
- 11289
+ 11290
106
141
- 11760
+ 11761
148
226
- 11289
+ 11290
226
373
- 10819
+ 10820
381
1456
- 10819
+ 10820
1464
@@ -4675,62 +4635,62 @@
1
2
- 16464
+ 16465
2
4
- 8937
+ 8938
4
5
- 7526
+ 7527
5
6
- 7526
+ 7527
6
8
- 11760
+ 11761
8
13
- 12230
+ 12231
13
17
- 10819
+ 10820
17
25
- 11289
+ 11290
25
31
- 11760
+ 11761
31
38
- 10819
+ 10820
38
52
- 10819
+ 10820
52
64
- 10819
+ 10820
65
@@ -4751,62 +4711,62 @@
1
2
- 16464
+ 16465
2
9
- 10819
+ 10820
9
16
- 11760
+ 11761
16
25
- 11289
+ 11290
25
40
- 10819
+ 10820
40
57
- 10819
+ 10820
58
71
- 10819
+ 10820
72
98
- 10819
+ 10820
101
140
- 11760
+ 11761
140
224
- 10819
+ 10820
224
360
- 10819
+ 10820
364
1185
- 10819
+ 10820
1254
@@ -4827,27 +4787,27 @@
1
2
- 16464
+ 16465
2
10
- 11289
+ 11290
10
14
- 10819
+ 10820
14
21
- 11289
+ 11290
22
31
- 11289
+ 11290
31
@@ -4857,27 +4817,27 @@
39
48
- 12230
+ 12231
48
56
- 11760
+ 11761
56
64
- 11760
+ 11761
64
72
- 10819
+ 10820
72
77
- 11289
+ 11290
77
@@ -4898,47 +4858,47 @@
1
2
- 581905
+ 581935
2
3
- 318001
+ 318018
3
4
- 199456
+ 199466
4
6
- 160882
+ 160890
6
10
- 190048
+ 190058
10
16
- 166057
+ 166065
16
25
- 168879
+ 168888
25
46
- 163704
+ 163713
46
169
- 159000
+ 159009
170
@@ -4959,42 +4919,42 @@
1
2
- 869799
+ 869845
2
3
- 280838
+ 280853
3
5
- 191459
+ 191469
5
8
- 181110
+ 181119
8
12
- 162293
+ 162302
12
18
- 166527
+ 166536
18
39
- 159941
+ 159949
39
299
- 103021
+ 103026
@@ -5010,47 +4970,47 @@
1
2
- 612482
+ 612514
2
3
- 313297
+ 313313
3
4
- 202749
+ 202760
4
6
- 184403
+ 184412
6
9
- 180639
+ 180649
9
13
- 166997
+ 167006
13
19
- 173583
+ 173592
19
29
- 167468
+ 167476
29
52
- 113370
+ 113376
@@ -5066,22 +5026,22 @@
1
2
- 1545788
+ 1545868
2
3
- 351401
+ 351419
3
5
- 164645
+ 164654
5
16
- 53157
+ 53159
@@ -5097,47 +5057,47 @@
1
2
- 586609
+ 586639
2
3
- 318942
+ 318958
3
4
- 201808
+ 201819
4
6
- 167468
+ 167476
6
9
- 160412
+ 160420
9
14
- 178287
+ 178297
14
21
- 176406
+ 176415
21
32
- 163234
+ 163243
32
61
- 159000
+ 159009
61
@@ -5212,7 +5172,7 @@
819
- 1546
+ 1548
2822
@@ -5244,7 +5204,7 @@
23
35
- 3292
+ 3293
38
@@ -5263,23 +5223,23 @@
73
- 83
- 2822
+ 84
+ 3293
- 83
- 95
- 2822
+ 84
+ 97
+ 3293
- 96
+ 98
101
- 3292
+ 2352
101
105
- 3292
+ 3293
106
@@ -5369,7 +5329,7 @@
587
- 831
+ 832
2822
@@ -5482,12 +5442,12 @@
7
11
- 3292
+ 3293
11
16
- 3292
+ 3293
16
@@ -5502,7 +5462,7 @@
24
27
- 3292
+ 3293
28
@@ -5512,7 +5472,7 @@
33
40
- 3292
+ 3293
40
@@ -5553,52 +5513,52 @@
1
2
- 589902
+ 589932
2
3
- 312356
+ 312372
3
4
- 198986
+ 198996
4
6
- 161352
+ 161361
6
10
- 189577
+ 189587
10
16
- 163704
+ 163713
16
25
- 171231
+ 170770
25
45
- 159471
+ 159949
45
160
- 159941
+ 159949
160
299
- 11289
+ 11290
@@ -5614,47 +5574,47 @@
1
2
- 881560
+ 881606
2
3
- 270019
+ 270033
3
4
- 123249
+ 123255
4
6
- 142065
+ 142073
6
10
- 195693
+ 195703
10
15
- 168409
+ 168417
15
26
- 165586
+ 165595
26
120
- 159471
+ 159479
121
299
- 11760
+ 11761
@@ -5670,22 +5630,22 @@
1
2
- 1538732
+ 1538812
2
3
- 349048
+ 349067
3
5
- 172172
+ 172181
5
10
- 57861
+ 57864
@@ -5701,47 +5661,47 @@
1
2
- 621890
+ 621922
2
3
- 304359
+ 304375
3
4
- 204631
+ 204641
4
6
- 186755
+ 186765
6
9
- 177817
+ 177826
9
13
- 169349
+ 169358
13
19
- 174524
+ 174533
19
29
- 162764
+ 162772
29
52
- 115722
+ 115728
@@ -5757,47 +5717,47 @@
1
2
- 596488
+ 596519
2
3
- 311415
+ 311431
3
4
- 198986
+ 198996
4
6
- 171701
+ 171710
6
9
- 157589
+ 157597
9
14
- 173583
+ 173592
14
21
- 180169
+ 180178
21
32
- 165116
+ 165124
32
60
- 159000
+ 159009
60
@@ -5862,7 +5822,7 @@
879
- 1081
+ 1082
3763
@@ -5871,7 +5831,7 @@
3763
- 1309
+ 1310
1590
3763
@@ -5894,7 +5854,7 @@
1
2
- 5644
+ 5645
2
@@ -5970,7 +5930,7 @@
1
2
- 5644
+ 5645
2
@@ -6009,7 +5969,7 @@
575
- 628
+ 627
3763
@@ -6081,7 +6041,7 @@
35
39
- 3292
+ 3293
39
@@ -6122,7 +6082,7 @@
1
2
- 5644
+ 5645
2
@@ -6192,11 +6152,11 @@
locations_stmt
- 3805462
+ 3805465
id
- 3805462
+ 3805465
container
@@ -6230,7 +6190,7 @@
1
2
- 3805462
+ 3805465
@@ -6246,7 +6206,7 @@
1
2
- 3805462
+ 3805465
@@ -6262,7 +6222,7 @@
1
2
- 3805462
+ 3805465
@@ -6278,7 +6238,7 @@
1
2
- 3805462
+ 3805465
@@ -6294,7 +6254,7 @@
1
2
- 3805462
+ 3805465
@@ -6958,7 +6918,7 @@
10
11
- 10500
+ 10501
11
@@ -8174,11 +8134,11 @@
locations_expr
- 13128112
+ 13128120
id
- 13128112
+ 13128120
container
@@ -8212,7 +8172,7 @@
1
2
- 13128112
+ 13128120
@@ -8228,7 +8188,7 @@
1
2
- 13128112
+ 13128120
@@ -8244,7 +8204,7 @@
1
2
- 13128112
+ 13128120
@@ -8260,7 +8220,7 @@
1
2
- 13128112
+ 13128120
@@ -8276,7 +8236,7 @@
1
2
- 13128112
+ 13128120
@@ -10096,23 +10056,23 @@
numlines
- 1406545
+ 1406618
element_id
- 1399488
+ 1399561
num_lines
- 102550
+ 102556
num_code
- 85615
+ 85620
num_comment
- 60213
+ 60216
@@ -10126,7 +10086,7 @@
1
2
- 1392432
+ 1392505
2
@@ -10147,7 +10107,7 @@
1
2
- 1393373
+ 1393446
2
@@ -10168,7 +10128,7 @@
1
2
- 1399488
+ 1399561
@@ -10184,17 +10144,17 @@
1
2
- 68680
+ 68684
2
3
- 12230
+ 12231
3
4
- 7526
+ 7527
4
@@ -10220,12 +10180,12 @@
1
2
- 71032
+ 71036
2
3
- 12230
+ 12231
3
@@ -10256,22 +10216,22 @@
1
2
- 70092
+ 70095
2
3
- 15053
+ 15054
3
4
- 10819
+ 10820
4
7
- 6585
+ 6586
@@ -10287,22 +10247,22 @@
1
2
- 53157
+ 53159
2
3
- 14582
+ 14583
3
5
- 6585
+ 6586
5
42
- 6585
+ 6586
44
@@ -10323,12 +10283,12 @@
1
2
- 53157
+ 53159
2
3
- 16934
+ 16935
3
@@ -10338,7 +10298,7 @@
5
8
- 6585
+ 6586
8
@@ -10359,7 +10319,7 @@
1
2
- 53627
+ 53630
2
@@ -10369,7 +10329,7 @@
3
5
- 7526
+ 7527
5
@@ -10379,7 +10339,7 @@
7
10
- 3292
+ 3293
@@ -10395,7 +10355,7 @@
1
2
- 34810
+ 34812
2
@@ -10436,7 +10396,7 @@
1
2
- 34810
+ 34812
2
@@ -10477,7 +10437,7 @@
1
2
- 34810
+ 34812
2
@@ -10512,31 +10472,31 @@
diagnostics
- 582773
+ 72312
id
- 582773
+ 72312
severity
- 209
+ 23
error_tag
- 7851
+ 80
error_message
- 56215
+ 127
full_error_message
- 582040
+ 62738
location
- 369218
+ 150
@@ -10550,7 +10510,7 @@
1
2
- 582773
+ 72312
@@ -10566,7 +10526,7 @@
1
2
- 582773
+ 72312
@@ -10582,7 +10542,7 @@
1
2
- 582773
+ 72312
@@ -10598,7 +10558,7 @@
1
2
- 582773
+ 72312
@@ -10614,7 +10574,7 @@
1
2
- 582773
+ 72312
@@ -10628,14 +10588,14 @@
12
- 254
- 255
- 104
+ 4
+ 5
+ 11
- 5313
- 5314
- 104
+ 6250
+ 6251
+ 11
@@ -10649,14 +10609,14 @@
12
- 5
- 6
- 104
+ 1
+ 2
+ 11
- 70
- 71
- 104
+ 6
+ 7
+ 11
@@ -10669,508 +10629,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
+ 127
+
+
+
+
+
+
+ error_message
+ error_tag
+
+
+ 12
+
+
+ 1
+ 2
+ 127
+
+
+
+
+
+
+ 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
+ 62726
+
+
+ 829
+ 830
+ 11
@@ -11186,7 +11026,7 @@
1
2
- 582040
+ 62738
@@ -11202,7 +11042,7 @@
1
2
- 582040
+ 62738
@@ -11218,7 +11058,7 @@
1
2
- 582040
+ 62738
@@ -11234,7 +11074,7 @@
1
2
- 582040
+ 62738
@@ -11250,17 +11090,12 @@
1
2
- 206959
+ 138
- 2
- 3
- 135774
-
-
- 3
- 13
- 26484
+ 6242
+ 6243
+ 11
@@ -11276,12 +11111,7 @@
1
2
- 361262
-
-
- 2
- 3
- 7955
+ 150
@@ -11297,12 +11127,12 @@
1
2
- 354458
+ 138
- 2
- 6
- 14760
+ 3
+ 4
+ 11
@@ -11318,12 +11148,12 @@
1
2
- 353830
+ 138
- 2
+ 5
6
- 15388
+ 11
@@ -11339,17 +11169,12 @@
1
2
- 207063
+ 138
- 2
- 3
- 135774
-
-
- 3
- 13
- 26380
+ 5414
+ 5415
+ 11
@@ -11359,15 +11184,15 @@
files
- 124189
+ 124196
id
- 124189
+ 124196
name
- 124189
+ 124196
@@ -11381,7 +11206,7 @@
1
2
- 124189
+ 124196
@@ -11397,7 +11222,7 @@
1
2
- 124189
+ 124196
@@ -11455,7 +11280,7 @@
containerparent
- 139243
+ 139250
parent
@@ -11463,7 +11288,7 @@
child
- 139243
+ 139250
@@ -11482,7 +11307,7 @@
2
3
- 3292
+ 3293
3
@@ -11518,7 +11343,7 @@
1
2
- 139243
+ 139250
@@ -11528,11 +11353,11 @@
fileannotations
- 5254886
+ 5253841
id
- 5019
+ 5018
kind
@@ -11540,11 +11365,11 @@
name
- 56112
+ 56101
value
- 47173
+ 47163
@@ -11563,7 +11388,7 @@
2
3
- 4845
+ 4844
@@ -11624,7 +11449,7 @@
936
937
- 1457
+ 1456
1083
@@ -11690,7 +11515,7 @@
1501
1502
- 1457
+ 1456
1504
@@ -11779,12 +11604,12 @@
1
2
- 9078
+ 9076
2
3
- 6372
+ 6370
3
@@ -11794,12 +11619,12 @@
5
9
- 4371
+ 4370
9
14
- 4082
+ 4081
14
@@ -11809,27 +11634,27 @@
18
20
- 4834
+ 4833
20
34
- 4325
+ 4324
34
128
- 4614
+ 4613
128
229
- 4221
+ 4220
229
387
- 4348
+ 4347
387
@@ -11850,7 +11675,7 @@
1
2
- 56112
+ 56101
@@ -11866,17 +11691,17 @@
1
2
- 9089
+ 9088
2
3
- 8257
+ 8255
3
4
- 2625
+ 2624
4
@@ -11886,37 +11711,37 @@
6
9
- 4232
+ 4231
9
14
- 4313
+ 4312
14
17
- 4232
+ 4231
17
22
- 4706
+ 4705
22
41
- 4313
+ 4312
41
82
- 4267
+ 4266
82
157
- 4209
+ 4208
158
@@ -11937,7 +11762,7 @@
1
2
- 7332
+ 7330
2
@@ -11947,7 +11772,7 @@
5
8
- 3411
+ 3410
8
@@ -11957,22 +11782,22 @@
15
17
- 2602
+ 2601
17
19
- 4244
+ 4243
19
34
- 3411
+ 3410
34
189
- 3712
+ 3711
189
@@ -11987,12 +11812,12 @@
266
321
- 3770
+ 3769
322
399
- 4047
+ 4046
399
@@ -12013,7 +11838,7 @@
1
2
- 47161
+ 47152
2
@@ -12034,17 +11859,17 @@
1
2
- 7355
+ 7353
2
5
- 2648
+ 2647
5
8
- 3596
+ 3595
8
@@ -12059,17 +11884,17 @@
17
19
- 3677
+ 3676
19
29
- 3596
+ 3595
29
39
- 3758
+ 3757
39
@@ -12079,7 +11904,7 @@
48
74
- 3654
+ 3653
74
@@ -12089,7 +11914,7 @@
102
119
- 3689
+ 3688
119
@@ -12104,15 +11929,15 @@
inmacroexpansion
- 109313317
+ 109313388
id
- 17941916
+ 17941927
inv
- 2682068
+ 2682069
@@ -12126,32 +11951,32 @@
1
3
- 1566018
+ 1566019
3
5
- 1071344
+ 1071345
5
6
- 1179814
+ 1179815
6
7
- 4800000
+ 4800003
7
8
- 6359380
+ 6359384
8
9
- 2595248
+ 2595250
9
@@ -12172,12 +11997,12 @@
1
2
- 371855
+ 371856
2
3
- 540116
+ 540113
3
@@ -12212,7 +12037,7 @@
11
337
- 223911
+ 223913
339
@@ -12232,15 +12057,15 @@
affectedbymacroexpansion
- 35540532
+ 35540555
id
- 5135277
+ 5135280
inv
- 2773181
+ 2773183
@@ -12254,7 +12079,7 @@
1
2
- 2804212
+ 2804214
2
@@ -12279,7 +12104,7 @@
12
50
- 405705
+ 405706
50
@@ -12305,7 +12130,7 @@
4
7
- 230823
+ 230824
7
@@ -12315,7 +12140,7 @@
9
12
- 250042
+ 250043
12
@@ -12345,7 +12170,7 @@
17
18
- 146328
+ 146329
18
@@ -12370,19 +12195,19 @@
macroinvocations
- 33894981
+ 33889080
id
- 33894981
+ 33889080
macro_id
- 81381
+ 81423
location
- 778557
+ 778830
kind
@@ -12400,7 +12225,7 @@
1
2
- 33894981
+ 33889080
@@ -12416,7 +12241,7 @@
1
2
- 33894981
+ 33889080
@@ -12432,7 +12257,7 @@
1
2
- 33894981
+ 33889080
@@ -12448,12 +12273,12 @@
1
2
- 17578
+ 17575
2
3
- 16977
+ 16973
3
@@ -12463,42 +12288,42 @@
4
5
- 4880
+ 4879
5
8
- 6048
+ 6047
8
14
- 6406
+ 6440
14
29
- 6291
+ 6313
29
- 72
- 6106
+ 73
+ 6220
- 72
- 247
- 6129
+ 73
+ 257
+ 6139
- 248
- 4166
- 6106
+ 257
+ 5769
+ 6116
- 4220
+ 6272
168296
- 1156
+ 1017
@@ -12514,32 +12339,32 @@
1
2
- 43506
+ 43498
2
3
- 10651
+ 10649
3
4
- 5285
+ 5284
4
6
- 6985
+ 7018
6
13
- 6626
+ 6636
13
66
- 6140
+ 6151
66
@@ -12560,12 +12385,12 @@
1
2
- 75553
+ 75538
2
3
- 5828
+ 5885
@@ -12581,37 +12406,37 @@
1
2
- 320982
+ 321115
2
3
- 177751
+ 177878
3
4
- 47300
+ 47313
4
5
- 59605
+ 59616
5
9
- 68533
+ 68542
9
23
- 58425
+ 58414
23
244365
- 45958
+ 45949
@@ -12627,12 +12452,12 @@
1
2
- 731280
+ 731563
2
350
- 47277
+ 47267
@@ -12648,7 +12473,7 @@
1
2
- 778557
+ 778830
@@ -12662,13 +12487,13 @@
12
- 20414
- 20415
+ 20464
+ 20465
11
- 2910446
- 2910447
+ 2910469
+ 2910470
11
@@ -12683,13 +12508,13 @@
12
- 2123
- 2124
+ 2128
+ 2129
11
- 5418
- 5419
+ 5423
+ 5424
11
@@ -12704,13 +12529,13 @@
12
- 6291
- 6292
+ 6315
+ 6316
11
- 61030
- 61031
+ 61043
+ 61044
11
@@ -12721,15 +12546,15 @@
macroparent
- 30455592
+ 30449647
id
- 30455592
+ 30449647
parent_id
- 23698199
+ 23693599
@@ -12743,7 +12568,7 @@
1
2
- 30455592
+ 30449647
@@ -12759,17 +12584,17 @@
1
2
- 18307171
+ 18303643
2
3
- 4540062
+ 4539159
3
88
- 850965
+ 850796
@@ -12779,15 +12604,15 @@
macrolocationbind
- 3984640
+ 3984515
id
- 2778886
+ 2778799
location
- 1988454
+ 1988392
@@ -12801,22 +12626,22 @@
1
2
- 2183104
+ 2183036
2
3
- 336443
+ 336432
3
7
- 229815
+ 229808
7
57
- 29523
+ 29522
@@ -12832,22 +12657,22 @@
1
2
- 1589588
+ 1589539
2
3
- 169647
+ 169641
3
8
- 154233
+ 154228
8
723
- 74984
+ 74982
@@ -12857,11 +12682,11 @@
macro_argument_unexpanded
- 86176782
+ 86159621
invocation
- 26568343
+ 26563044
argument_index
@@ -12869,7 +12694,7 @@
text
- 326094
+ 326029
@@ -12883,22 +12708,22 @@
1
2
- 7436793
+ 7435302
2
3
- 10861692
+ 10859530
3
4
- 6256808
+ 6255563
4
67
- 2013048
+ 2012648
@@ -12914,22 +12739,22 @@
1
2
- 7508010
+ 7506504
2
3
- 11011561
+ 11009369
3
4
- 6087232
+ 6086021
4
67
- 1961538
+ 1961148
@@ -12954,7 +12779,7 @@
715085
- 2297335
+ 2297334
34
@@ -12997,57 +12822,57 @@
1
2
- 40858
+ 40850
2
3
- 65607
+ 65594
3
4
- 15184
+ 15181
4
5
- 45102
+ 45093
5
8
- 25569
+ 25576
8
12
- 16075
+ 16060
12
16
- 22297
+ 22292
16
23
- 26518
+ 26512
23
43
- 24748
+ 24743
43
164
- 24459
+ 24454
164
521384
- 19671
+ 19667
@@ -13063,17 +12888,17 @@
1
2
- 235830
+ 235783
2
3
- 79728
+ 79712
3
9
- 10535
+ 10533
@@ -13083,11 +12908,11 @@
macro_argument_expanded
- 86176782
+ 86159621
invocation
- 26568343
+ 26563044
argument_index
@@ -13095,7 +12920,7 @@
text
- 197597
+ 197580
@@ -13109,22 +12934,22 @@
1
2
- 7436793
+ 7435302
2
3
- 10861692
+ 10859530
3
4
- 6256808
+ 6255563
4
67
- 2013048
+ 2012648
@@ -13140,22 +12965,22 @@
1
2
- 10747743
+ 10745593
2
3
- 9374139
+ 9372273
3
4
- 5306998
+ 5305941
4
9
- 1139462
+ 1139235
@@ -13180,7 +13005,7 @@
715085
- 2297335
+ 2297334
34
@@ -13206,7 +13031,7 @@
870
- 13877
+ 13879
46
@@ -13223,62 +13048,62 @@
1
2
- 24552
+ 24547
2
3
- 41147
+ 41151
3
4
- 6927
+ 6925
4
5
- 16364
+ 16361
5
6
- 2995
+ 2994
6
7
- 23291
+ 23286
7
9
- 15982
+ 15991
9
15
- 16699
+ 16696
15
31
- 15589
+ 15586
31
97
- 15080
+ 15077
97
775
- 15485
+ 15482
775
- 1052916
- 3481
+ 1052906
+ 3480
@@ -13294,17 +13119,17 @@
1
2
- 99989
+ 99992
2
3
- 82850
+ 82834
3
66
- 14756
+ 14753
@@ -13314,19 +13139,19 @@
functions
- 4726273
+ 4726519
id
- 4726273
+ 4726519
name
- 1934352
+ 1934453
kind
- 3292
+ 3293
@@ -13340,7 +13165,7 @@
1
2
- 4726273
+ 4726519
@@ -13356,7 +13181,7 @@
1
2
- 4726273
+ 4726519
@@ -13372,22 +13197,22 @@
1
2
- 1516622
+ 1516701
2
3
- 154296
+ 154304
3
5
- 151003
+ 151011
5
1724
- 112429
+ 112435
@@ -13403,7 +13228,7 @@
1
2
- 1933881
+ 1933982
2
@@ -13510,15 +13335,15 @@
function_entry_point
- 1176981
+ 1177043
id
- 1167103
+ 1167163
entry_point
- 1176981
+ 1177043
@@ -13532,12 +13357,12 @@
1
2
- 1157224
+ 1157284
2
3
- 9878
+ 9879
@@ -13553,7 +13378,7 @@
1
2
- 1176981
+ 1177043
@@ -13563,15 +13388,15 @@
function_return_type
- 4734741
+ 4734987
id
- 4726273
+ 4726519
return_type
- 1016569
+ 1016622
@@ -13585,7 +13410,7 @@
1
2
- 4719217
+ 4719463
2
@@ -13606,22 +13431,22 @@
1
2
- 523103
+ 523130
2
3
- 390445
+ 390465
3
11
- 78559
+ 78563
11
2516
- 24461
+ 24462
@@ -13639,7 +13464,7 @@
traits
- 1
+ 2
handle
@@ -13707,9 +13532,9 @@
12
- 2
- 3
- 1
+ 1
+ 2
+ 2
@@ -13723,9 +13548,9 @@
12
- 2
- 3
- 1
+ 1
+ 2
+ 2
@@ -13739,9 +13564,9 @@
12
- 2
- 3
- 1
+ 1
+ 2
+ 2
@@ -13943,48 +13768,48 @@
purefunctions
- 99446
+ 99447
id
- 99446
+ 99447
function_deleted
- 140654
+ 140661
id
- 140654
+ 140661
function_defaulted
- 74325
+ 74329
id
- 74325
+ 74329
member_function_this_type
- 553641
+ 553316
id
- 553641
+ 553316
this_type
- 189690
+ 189579
@@ -13998,7 +13823,7 @@
1
2
- 553641
+ 553316
@@ -14014,32 +13839,32 @@
1
2
- 68526
+ 68486
2
3
- 45414
+ 45387
3
4
- 30475
+ 30458
4
5
- 15537
+ 15528
5
7
- 15607
+ 15598
7
66
- 14128
+ 14119
@@ -14049,27 +13874,27 @@
fun_decls
- 5102136
+ 5102402
id
- 5096962
+ 5097227
function
- 4578563
+ 4578801
type_id
- 1013276
+ 1013329
name
- 1836035
+ 1836130
location
- 3461324
+ 3461504
@@ -14083,7 +13908,7 @@
1
2
- 5096962
+ 5097227
@@ -14099,7 +13924,7 @@
1
2
- 5091787
+ 5092052
2
@@ -14120,7 +13945,7 @@
1
2
- 5096962
+ 5097227
@@ -14136,7 +13961,7 @@
1
2
- 5096962
+ 5097227
@@ -14152,17 +13977,17 @@
1
2
- 4141075
+ 4141291
2
3
- 363161
+ 363180
3
7
- 74325
+ 74329
@@ -14178,12 +14003,12 @@
1
2
- 4536696
+ 4536932
2
5
- 41867
+ 41869
@@ -14199,7 +14024,7 @@
1
2
- 4578563
+ 4578801
@@ -14215,12 +14040,12 @@
1
2
- 4197996
+ 4198214
2
4
- 379155
+ 379175
4
@@ -14241,22 +14066,22 @@
1
2
- 445954
+ 445977
2
3
- 453481
+ 453505
3
9
- 79500
+ 79504
9
2768
- 34340
+ 34342
@@ -14272,22 +14097,22 @@
1
2
- 530629
+ 530657
2
3
- 381978
+ 381998
3
11
- 77148
+ 77152
11
2477
- 23520
+ 23522
@@ -14303,17 +14128,17 @@
1
2
- 883912
+ 883958
2
5
- 90319
+ 90324
5
822
- 39044
+ 39046
@@ -14329,22 +14154,22 @@
1
2
- 779480
+ 779520
2
3
- 133127
+ 133134
3
11
- 78089
+ 78093
11
2030
- 22579
+ 22581
@@ -14360,27 +14185,27 @@
1
2
- 1245192
+ 1245257
2
3
- 269548
+ 269562
3
4
- 80441
+ 80445
4
6
- 138772
+ 138780
6
1758
- 102080
+ 102085
@@ -14396,22 +14221,22 @@
1
2
- 1425832
+ 1425906
2
3
- 153355
+ 153363
3
5
- 145358
+ 145366
5
1708
- 111488
+ 111494
@@ -14427,17 +14252,17 @@
1
2
- 1615880
+ 1615964
2
4
- 135009
+ 135016
4
954
- 85145
+ 85149
@@ -14453,27 +14278,27 @@
1
2
- 1266831
+ 1266897
2
3
- 296362
+ 296377
3
4
- 79500
+ 79504
4
8
- 139243
+ 139250
8
664
- 54097
+ 54100
@@ -14489,17 +14314,17 @@
1
2
- 2995611
+ 2995767
2
4
- 302007
+ 302023
4
55
- 163704
+ 163713
@@ -14515,17 +14340,17 @@
1
2
- 3063351
+ 3063511
2
6
- 268607
+ 268621
6
55
- 129364
+ 129371
@@ -14541,12 +14366,12 @@
1
2
- 3245873
+ 3246042
2
27
- 215450
+ 215461
@@ -14562,12 +14387,12 @@
1
2
- 3285388
+ 3285559
2
13
- 175935
+ 175944
@@ -14577,22 +14402,22 @@
fun_def
- 1963988
+ 1964090
id
- 1963988
+ 1964090
fun_specialized
- 26343
+ 26344
id
- 26343
+ 26344
@@ -14610,11 +14435,11 @@
fun_decl_specifiers
- 2937280
+ 2937433
id
- 1710904
+ 1710993
name
@@ -14632,17 +14457,17 @@
1
2
- 503345
+ 503371
2
3
- 1188742
+ 1188804
3
4
- 18816
+ 18817
@@ -14814,26 +14639,26 @@
fun_decl_empty_throws
- 1978101
+ 1978204
fun_decl
- 1978101
+ 1978204
fun_decl_noexcept
- 61239
+ 61252
fun_decl
- 61239
+ 61252
constant
- 61135
+ 61148
@@ -14847,7 +14672,7 @@
1
2
- 61239
+ 61252
@@ -14863,7 +14688,7 @@
1
2
- 61030
+ 61043
2
@@ -14878,11 +14703,11 @@
fun_decl_empty_noexcept
- 888146
+ 888192
fun_decl
- 888146
+ 888192
@@ -14987,11 +14812,11 @@
param_decl_bind
- 7472094
+ 7472483
id
- 7472094
+ 7472483
index
@@ -14999,7 +14824,7 @@
fun_decl
- 4286434
+ 4286657
@@ -15013,7 +14838,7 @@
1
2
- 7472094
+ 7472483
@@ -15029,7 +14854,7 @@
1
2
- 7472094
+ 7472483
@@ -15207,22 +15032,22 @@
1
2
- 2409002
+ 2409127
2
3
- 1071608
+ 1071664
3
4
- 506638
+ 506664
4
18
- 299184
+ 299200
@@ -15238,22 +15063,22 @@
1
2
- 2409002
+ 2409127
2
3
- 1071608
+ 1071664
3
4
- 506638
+ 506664
4
18
- 299184
+ 299200
@@ -15263,27 +15088,27 @@
var_decls
- 8611913
+ 8612362
id
- 8543232
+ 8543677
variable
- 7520077
+ 7520468
type_id
- 2430641
+ 2430768
name
- 672695
+ 672730
location
- 5365099
+ 5365378
@@ -15297,7 +15122,7 @@
1
2
- 8543232
+ 8543677
@@ -15313,12 +15138,12 @@
1
2
- 8474552
+ 8474993
2
3
- 68680
+ 68684
@@ -15334,7 +15159,7 @@
1
2
- 8543232
+ 8543677
@@ -15350,7 +15175,7 @@
1
2
- 8543232
+ 8543677
@@ -15366,17 +15191,17 @@
1
2
- 6658274
+ 6658620
2
3
- 707035
+ 707072
3
7
- 154767
+ 154775
@@ -15392,12 +15217,12 @@
1
2
- 7346963
+ 7347346
2
4
- 173113
+ 173122
@@ -15413,12 +15238,12 @@
1
2
- 7402943
+ 7403328
2
3
- 117133
+ 117139
@@ -15434,12 +15259,12 @@
1
2
- 6967337
+ 6967700
2
4
- 552739
+ 552768
@@ -15455,27 +15280,27 @@
1
2
- 1505802
+ 1505881
2
3
- 516046
+ 516073
3
4
- 98787
+ 98792
4
7
- 188636
+ 188646
7
780
- 121367
+ 121373
@@ -15491,22 +15316,22 @@
1
2
- 1640342
+ 1640427
2
3
- 491114
+ 491140
3
7
- 188166
+ 188176
7
742
- 111018
+ 111024
@@ -15522,17 +15347,17 @@
1
2
- 1918828
+ 1918928
2
3
- 388563
+ 388584
3
128
- 123249
+ 123255
@@ -15548,22 +15373,22 @@
1
2
- 1743833
+ 1743924
2
3
- 406910
+ 406931
3
8
- 190048
+ 190058
8
595
- 89849
+ 89854
@@ -15579,37 +15404,37 @@
1
2
- 343874
+ 343892
2
3
- 87497
+ 87502
3
4
- 48923
+ 48925
4
6
- 52216
+ 52218
6
12
- 52686
+ 52689
12
33
- 50804
+ 50807
34
3281
- 36692
+ 36694
@@ -15625,37 +15450,37 @@
1
2
- 371628
+ 371648
2
3
- 78559
+ 78563
3
4
- 45630
+ 45632
4
6
- 49864
+ 49866
6
14
- 53627
+ 53630
14
56
- 51275
+ 51278
56
3198
- 22109
+ 22110
@@ -15671,27 +15496,27 @@
1
2
- 460537
+ 460561
2
3
- 94553
+ 94558
3
5
- 47041
+ 47044
5
19
- 51275
+ 51278
19
1979
- 19287
+ 19288
@@ -15707,32 +15532,32 @@
1
2
- 381978
+ 381998
2
3
- 91260
+ 91265
3
5
- 60213
+ 60216
5
9
- 51745
+ 51748
9
21
- 50804
+ 50807
21
1020
- 36692
+ 36694
@@ -15748,17 +15573,17 @@
1
2
- 4535755
+ 4535991
2
3
- 550387
+ 550415
3
1783
- 278956
+ 278971
@@ -15774,17 +15599,17 @@
1
2
- 4939842
+ 4940100
2
17
- 414436
+ 414458
17
1779
- 10819
+ 10820
@@ -15800,12 +15625,12 @@
1
2
- 5016520
+ 5016782
2
1561
- 348578
+ 348596
@@ -15821,7 +15646,7 @@
1
2
- 5360865
+ 5361144
2
@@ -15836,22 +15661,22 @@
var_def
- 4083685
+ 4083897
id
- 4083685
+ 4083897
var_decl_specifiers
- 334936
+ 334953
id
- 334936
+ 334953
name
@@ -15869,7 +15694,7 @@
1
2
- 334936
+ 334953
@@ -15916,19 +15741,19 @@
type_decls
- 3283977
+ 3284148
id
- 3283977
+ 3284148
type_id
- 3233172
+ 3233340
location
- 3204006
+ 3204173
@@ -15942,7 +15767,7 @@
1
2
- 3283977
+ 3284148
@@ -15958,7 +15783,7 @@
1
2
- 3283977
+ 3284148
@@ -15974,12 +15799,12 @@
1
2
- 3191305
+ 3191471
2
5
- 41867
+ 41869
@@ -15995,12 +15820,12 @@
1
2
- 3191305
+ 3191471
2
5
- 41867
+ 41869
@@ -16016,12 +15841,12 @@
1
2
- 3163080
+ 3163244
2
20
- 40926
+ 40928
@@ -16037,12 +15862,12 @@
1
2
- 3163080
+ 3163244
2
20
- 40926
+ 40928
@@ -16052,33 +15877,33 @@
type_def
- 2660675
+ 2660813
id
- 2660675
+ 2660813
type_decl_top
- 755959
+ 755998
type_decl
- 755959
+ 755998
namespace_decls
- 306972
+ 306973
id
- 306972
+ 306973
namespace_id
@@ -16086,11 +15911,11 @@
location
- 306972
+ 306973
bodylocation
- 306972
+ 306973
@@ -16104,7 +15929,7 @@
1
2
- 306972
+ 306973
@@ -16120,7 +15945,7 @@
1
2
- 306972
+ 306973
@@ -16136,7 +15961,7 @@
1
2
- 306972
+ 306973
@@ -16350,7 +16175,7 @@
1
2
- 306972
+ 306973
@@ -16366,7 +16191,7 @@
1
2
- 306972
+ 306973
@@ -16382,7 +16207,7 @@
1
2
- 306972
+ 306973
@@ -16398,7 +16223,7 @@
1
2
- 306972
+ 306973
@@ -16414,7 +16239,7 @@
1
2
- 306972
+ 306973
@@ -16430,7 +16255,7 @@
1
2
- 306972
+ 306973
@@ -16440,19 +16265,19 @@
usings
- 374921
+ 374941
id
- 374921
+ 374941
element_id
- 318471
+ 318488
location
- 249791
+ 249804
@@ -16466,7 +16291,7 @@
1
2
- 374921
+ 374941
@@ -16482,7 +16307,7 @@
1
2
- 374921
+ 374941
@@ -16498,12 +16323,12 @@
1
2
- 263903
+ 263917
2
3
- 53157
+ 53159
3
@@ -16524,12 +16349,12 @@
1
2
- 263903
+ 263917
2
3
- 53157
+ 53159
3
@@ -16550,22 +16375,22 @@
1
2
- 203690
+ 203700
2
4
- 11289
+ 11290
4
5
- 31517
+ 31519
5
11
- 3292
+ 3293
@@ -16581,22 +16406,22 @@
1
2
- 203690
+ 203700
2
4
- 11289
+ 11290
4
5
- 31517
+ 31519
5
11
- 3292
+ 3293
@@ -16606,15 +16431,15 @@
using_container
- 478195
+ 478100
parent
- 11298
+ 11296
child
- 303207
+ 303147
@@ -16684,17 +16509,17 @@
1
2
- 223629
+ 223585
2
3
- 52990
+ 52979
3
11
- 24401
+ 24396
13
@@ -16709,15 +16534,15 @@
static_asserts
- 130417
+ 130418
id
- 130417
+ 130418
condition
- 130417
+ 130418
message
@@ -16743,7 +16568,7 @@
1
2
- 130417
+ 130418
@@ -16759,7 +16584,7 @@
1
2
- 130417
+ 130418
@@ -16775,7 +16600,7 @@
1
2
- 130417
+ 130418
@@ -16791,7 +16616,7 @@
1
2
- 130417
+ 130418
@@ -16807,7 +16632,7 @@
1
2
- 130417
+ 130418
@@ -16823,7 +16648,7 @@
1
2
- 130417
+ 130418
@@ -16839,7 +16664,7 @@
1
2
- 130417
+ 130418
@@ -16855,7 +16680,7 @@
1
2
- 130417
+ 130418
@@ -17327,15 +17152,15 @@
params
- 6825742
+ 6826097
id
- 6660155
+ 6660502
function
- 3940208
+ 3940413
index
@@ -17343,7 +17168,7 @@
type_id
- 2234478
+ 2234594
@@ -17357,7 +17182,7 @@
1
2
- 6660155
+ 6660502
@@ -17373,7 +17198,7 @@
1
2
- 6660155
+ 6660502
@@ -17389,12 +17214,12 @@
1
2
- 6535025
+ 6535365
2
4
- 125130
+ 125137
@@ -17410,22 +17235,22 @@
1
2
- 2303158
+ 2303278
2
3
- 960590
+ 960640
3
4
- 433253
+ 433276
4
18
- 243205
+ 243217
@@ -17441,22 +17266,22 @@
1
2
- 2303158
+ 2303278
2
3
- 960590
+ 960640
3
4
- 433253
+ 433276
4
18
- 243205
+ 243217
@@ -17472,22 +17297,22 @@
1
2
- 2605636
+ 2605772
2
3
- 831225
+ 831269
3
4
- 349519
+ 349537
4
12
- 153826
+ 153834
@@ -17741,22 +17566,22 @@
1
2
- 1525560
+ 1525639
2
3
- 446425
+ 446448
3
8
- 171701
+ 171710
8
522
- 90790
+ 90795
@@ -17772,22 +17597,22 @@
1
2
- 1749008
+ 1749099
2
3
- 250731
+ 250745
3
9
- 169820
+ 169829
9
506
- 64917
+ 64920
@@ -17803,17 +17628,17 @@
1
2
- 1801694
+ 1801788
2
3
- 353282
+ 353301
3
13
- 79500
+ 79504
@@ -17823,7 +17648,7 @@
overrides
- 159823
+ 159824
new
@@ -17919,7 +17744,7 @@
type_id
- 326293
+ 326294
name
@@ -17937,7 +17762,7 @@
1
2
- 1048372
+ 1048373
2
@@ -18087,19 +17912,19 @@
globalvariables
- 300716
+ 318724
id
- 300708
+ 318724
type_id
- 1405
+ 7852
name
- 294738
+ 86905
@@ -18113,12 +17938,7 @@
1
2
- 300700
-
-
- 2
- 3
- 8
+ 318724
@@ -18134,7 +17954,7 @@
1
2
- 300708
+ 318724
@@ -18150,27 +17970,32 @@
1
2
- 977
+ 5130
2
3
- 159
+ 209
3
- 7
- 114
+ 4
+ 628
- 7
- 77
- 106
+ 4
+ 9
+ 628
- 83
- 169397
- 49
+ 18
+ 31
+ 628
+
+
+ 35
+ 1226
+ 628
@@ -18186,27 +18011,32 @@
1
2
- 1010
+ 5130
2
3
- 135
+ 209
3
- 7
- 112
+ 4
+ 628
- 7
- 105
- 106
+ 4
+ 9
+ 628
- 106
- 168448
- 42
+ 14
+ 25
+ 628
+
+
+ 35
+ 209
+ 628
@@ -18222,12 +18052,17 @@
1
2
- 290989
+ 75911
2
- 33
- 3749
+ 11
+ 6596
+
+
+ 11
+ 449
+ 4397
@@ -18243,12 +18078,12 @@
1
2
- 294142
+ 76644
2
- 12
- 596
+ 3
+ 10261
@@ -18266,7 +18101,7 @@
type_id
- 37905
+ 37909
name
@@ -18326,7 +18161,7 @@
3
4
- 2479
+ 2483
4
@@ -18336,12 +18171,12 @@
7
18
- 2878
+ 2874
18
15847
- 2513
+ 2517
@@ -18367,7 +18202,7 @@
3
5
- 2946
+ 2950
5
@@ -18449,11 +18284,11 @@
autoderivation
- 149488
+ 149519
var
- 149488
+ 149519
derivation_type
@@ -18471,7 +18306,7 @@
1
2
- 149488
+ 149519
@@ -18537,7 +18372,7 @@
name
- 240334
+ 240335
location
@@ -19272,7 +19107,7 @@
1
2
- 240334
+ 240335
@@ -19288,7 +19123,7 @@
1
2
- 240334
+ 240335
@@ -19414,23 +19249,23 @@
builtintypes
- 22109
+ 22110
id
- 22109
+ 22110
name
- 22109
+ 22110
kind
- 22109
+ 22110
size
- 3292
+ 3293
sign
@@ -19452,7 +19287,7 @@
1
2
- 22109
+ 22110
@@ -19468,7 +19303,7 @@
1
2
- 22109
+ 22110
@@ -19484,7 +19319,7 @@
1
2
- 22109
+ 22110
@@ -19500,7 +19335,7 @@
1
2
- 22109
+ 22110
@@ -19516,7 +19351,7 @@
1
2
- 22109
+ 22110
@@ -19532,7 +19367,7 @@
1
2
- 22109
+ 22110
@@ -19548,7 +19383,7 @@
1
2
- 22109
+ 22110
@@ -19564,7 +19399,7 @@
1
2
- 22109
+ 22110
@@ -19580,7 +19415,7 @@
1
2
- 22109
+ 22110
@@ -19596,7 +19431,7 @@
1
2
- 22109
+ 22110
@@ -19612,7 +19447,7 @@
1
2
- 22109
+ 22110
@@ -19628,7 +19463,7 @@
1
2
- 22109
+ 22110
@@ -19644,7 +19479,7 @@
1
2
- 22109
+ 22110
@@ -19660,7 +19495,7 @@
1
2
- 22109
+ 22110
@@ -19676,7 +19511,7 @@
1
2
- 22109
+ 22110
@@ -20126,15 +19961,15 @@
derivedtypes
- 4413446
+ 4413676
id
- 4413446
+ 4413676
name
- 2205312
+ 2205427
kind
@@ -20142,7 +19977,7 @@
type_id
- 2729356
+ 2729498
@@ -20156,7 +19991,7 @@
1
2
- 4413446
+ 4413676
@@ -20172,7 +20007,7 @@
1
2
- 4413446
+ 4413676
@@ -20188,7 +20023,7 @@
1
2
- 4413446
+ 4413676
@@ -20204,17 +20039,17 @@
1
2
- 1935763
+ 1935864
2
5
- 171231
+ 171240
5
1173
- 98317
+ 98322
@@ -20230,7 +20065,7 @@
1
2
- 2204371
+ 2204486
2
@@ -20251,17 +20086,17 @@
1
2
- 1935763
+ 1935864
2
5
- 171231
+ 171240
5
1155
- 98317
+ 98322
@@ -20400,22 +20235,22 @@
1
2
- 1685972
+ 1686060
2
3
- 568733
+ 568763
3
4
- 367395
+ 367414
4
54
- 107254
+ 107260
@@ -20431,22 +20266,22 @@
1
2
- 1697262
+ 1697350
2
3
- 561206
+ 561236
3
4
- 364572
+ 364591
4
54
- 106314
+ 106319
@@ -20462,22 +20297,22 @@
1
2
- 1690206
+ 1690294
2
3
- 572496
+ 572526
3
4
- 366454
+ 366473
4
6
- 100198
+ 100203
@@ -20487,11 +20322,11 @@
pointerishsize
- 3314342
+ 3312399
id
- 3314342
+ 3312399
size
@@ -20513,7 +20348,7 @@
1
2
- 3314342
+ 3312399
@@ -20529,7 +20364,7 @@
1
2
- 3314342
+ 3312399
@@ -20603,19 +20438,19 @@
arraysizes
- 71503
+ 71507
id
- 71503
+ 71507
num_elements
- 23520
+ 23522
bytesize
- 26343
+ 26344
alignment
@@ -20633,7 +20468,7 @@
1
2
- 71503
+ 71507
@@ -20649,7 +20484,7 @@
1
2
- 71503
+ 71507
@@ -20665,7 +20500,7 @@
1
2
- 71503
+ 71507
@@ -20686,7 +20521,7 @@
2
3
- 15053
+ 15054
3
@@ -20722,7 +20557,7 @@
1
2
- 18346
+ 18347
2
@@ -20753,7 +20588,7 @@
1
2
- 18346
+ 18347
2
@@ -20789,12 +20624,12 @@
2
3
- 16934
+ 16935
3
4
- 3292
+ 3293
4
@@ -20820,12 +20655,12 @@
1
2
- 21639
+ 21640
2
3
- 3292
+ 3293
3
@@ -20846,12 +20681,12 @@
1
2
- 22109
+ 22110
2
3
- 3292
+ 3293
4
@@ -20954,15 +20789,15 @@
typedefbase
- 1724181
+ 1736730
id
- 1724181
+ 1736730
type_id
- 803746
+ 810523
@@ -20976,7 +20811,7 @@
1
2
- 1724181
+ 1736730
@@ -20992,22 +20827,22 @@
1
2
- 623380
+ 629130
2
3
- 84307
+ 85019
3
6
- 64497
+ 64576
6
5443
- 31560
+ 31797
@@ -21017,19 +20852,19 @@
decltypes
- 355640
+ 355894
id
- 23953
+ 23951
expr
- 355640
+ 355894
base_type
- 17181
+ 17180
parentheses_would_change_meaning
@@ -21047,12 +20882,12 @@
1
2
- 5961
+ 5960
2
3
- 7492
+ 7491
3
@@ -21067,12 +20902,12 @@
7
18
- 1999
+ 1980
18
42
- 2017
+ 2035
42
@@ -21093,7 +20928,7 @@
1
2
- 23953
+ 23951
@@ -21109,7 +20944,7 @@
1
2
- 23953
+ 23951
@@ -21125,7 +20960,7 @@
1
2
- 355640
+ 355894
@@ -21141,7 +20976,7 @@
1
2
- 355640
+ 355894
@@ -21157,7 +20992,7 @@
1
2
- 355640
+ 355894
@@ -21204,7 +21039,7 @@
2
3
- 7348
+ 7347
3
@@ -21245,7 +21080,7 @@
1
2
- 17181
+ 17180
@@ -21275,8 +21110,8 @@
12
- 19747
- 19748
+ 19762
+ 19763
18
@@ -21303,15 +21138,15 @@
usertypes
- 5342989
+ 5343268
id
- 5342989
+ 5343268
name
- 1383024
+ 1383096
kind
@@ -21329,7 +21164,7 @@
1
2
- 5342989
+ 5343268
@@ -21345,7 +21180,7 @@
1
2
- 5342989
+ 5343268
@@ -21361,27 +21196,27 @@
1
2
- 1001986
+ 1002039
2
3
- 161352
+ 161361
3
7
- 107725
+ 107730
7
80
- 104432
+ 104437
80
885
- 7526
+ 7527
@@ -21397,17 +21232,17 @@
1
2
- 1240488
+ 1240552
2
3
- 127012
+ 127019
3
7
- 15523
+ 15524
@@ -21549,11 +21384,11 @@
usertypesize
- 1755594
+ 1755685
id
- 1755594
+ 1755685
size
@@ -21575,7 +21410,7 @@
1
2
- 1755594
+ 1755685
@@ -21591,7 +21426,7 @@
1
2
- 1755594
+ 1755685
@@ -21607,7 +21442,7 @@
1
2
- 3292
+ 3293
2
@@ -21755,11 +21590,11 @@
usertype_final
- 9526
+ 9528
id
- 9526
+ 9528
@@ -21819,15 +21654,15 @@
mangled_name
- 5264430
+ 5301398
id
- 5264430
+ 5301398
mangled_name
- 1235313
+ 1272072
@@ -21841,7 +21676,7 @@
1
2
- 5264430
+ 5301398
@@ -21857,32 +21692,32 @@
1
2
- 731027
+ 767759
2
3
- 178287
+ 178297
3
4
- 84674
+ 84679
4
- 6
- 86086
+ 7
+ 114787
- 6
- 13
- 93142
+ 7
+ 25
+ 95499
- 13
+ 25
885
- 62094
+ 31049
@@ -21892,59 +21727,59 @@
is_pod_class
- 554326
+ 554216
id
- 554326
+ 554216
is_standard_layout_class
- 1295997
+ 1296064
id
- 1295997
+ 1296064
is_complete
- 1694439
+ 1694528
id
- 1694439
+ 1694528
is_class_template
- 405028
+ 405049
id
- 405028
+ 405049
class_instantiation
- 1121943
+ 1122001
to
- 1121943
+ 1122001
from
- 170761
+ 170770
@@ -21958,7 +21793,7 @@
1
2
- 1121943
+ 1122001
@@ -21974,42 +21809,42 @@
1
2
- 58802
+ 58805
2
3
- 30106
+ 30108
3
4
- 16464
+ 16465
4
5
- 14582
+ 14583
5
7
- 15523
+ 15524
7
13
- 13171
+ 13172
13
29
- 13171
+ 13172
30
84
- 8937
+ 8938
@@ -22019,11 +21854,11 @@
class_template_argument
- 2978113
+ 2977520
type_id
- 1355713
+ 1355443
index
@@ -22031,7 +21866,7 @@
arg_type
- 863386
+ 863214
@@ -22045,27 +21880,27 @@
1
2
- 551562
+ 551453
2
3
- 411593
+ 411511
3
4
- 246019
+ 245970
4
7
- 122356
+ 122331
7
113
- 24182
+ 24177
@@ -22081,22 +21916,22 @@
1
2
- 577421
+ 577306
2
3
- 424939
+ 424854
3
4
- 257884
+ 257833
4
113
- 95467
+ 95448
@@ -22117,7 +21952,7 @@
2
3
- 821
+ 820
3
@@ -22163,7 +21998,7 @@
2
3
- 821
+ 820
3
@@ -22204,27 +22039,27 @@
1
2
- 535649
+ 535542
2
3
- 181070
+ 181034
3
4
- 52573
+ 52563
4
10
- 65688
+ 65675
10
11334
- 28403
+ 28397
@@ -22240,17 +22075,17 @@
1
2
- 755682
+ 755532
2
3
- 85741
+ 85724
3
22
- 21961
+ 21957
@@ -22260,11 +22095,11 @@
class_template_argument_value
- 508520
+ 508546
type_id
- 316590
+ 316606
index
@@ -22272,7 +22107,7 @@
arg_value
- 508520
+ 508546
@@ -22286,12 +22121,12 @@
1
2
- 261081
+ 261094
2
3
- 53627
+ 53630
3
@@ -22312,17 +22147,17 @@
1
2
- 200397
+ 200407
2
3
- 81852
+ 81856
3
5
- 29165
+ 29167
5
@@ -22405,7 +22240,7 @@
1
2
- 508520
+ 508546
@@ -22421,7 +22256,7 @@
1
2
- 508520
+ 508546
@@ -22431,15 +22266,15 @@
is_proxy_class_for
- 65387
+ 65391
id
- 65387
+ 65391
templ_param_id
- 65387
+ 65391
@@ -22453,7 +22288,7 @@
1
2
- 65387
+ 65391
@@ -22469,7 +22304,7 @@
1
2
- 65387
+ 65391
@@ -22479,11 +22314,11 @@
type_mentions
- 4011508
+ 4011511
id
- 4011508
+ 4011511
type_id
@@ -22491,7 +22326,7 @@
location
- 3978135
+ 3978138
kind
@@ -22509,7 +22344,7 @@
1
2
- 4011508
+ 4011511
@@ -22525,7 +22360,7 @@
1
2
- 4011508
+ 4011511
@@ -22541,7 +22376,7 @@
1
2
- 4011508
+ 4011511
@@ -22675,7 +22510,7 @@
1
2
- 3944762
+ 3944765
2
@@ -22696,7 +22531,7 @@
1
2
- 3944762
+ 3944765
2
@@ -22717,7 +22552,7 @@
1
2
- 3978135
+ 3978138
@@ -22775,26 +22610,26 @@
is_function_template
- 1413601
+ 1413674
id
- 1413601
+ 1413674
function_instantiation
- 906422
+ 905891
to
- 906422
+ 905891
from
- 146002
+ 145917
@@ -22808,7 +22643,7 @@
1
2
- 906422
+ 905891
@@ -22824,27 +22659,27 @@
1
2
- 101152
+ 101092
2
3
- 14480
+ 14472
3
6
- 12014
+ 12007
6
21
- 12049
+ 12042
22
869
- 6306
+ 6302
@@ -22854,11 +22689,11 @@
function_template_argument
- 2339815
+ 2338443
function_id
- 1318394
+ 1317621
index
@@ -22866,7 +22701,7 @@
arg_type
- 305041
+ 304862
@@ -22880,22 +22715,22 @@
1
2
- 679667
+ 679268
2
3
- 388084
+ 387856
3
4
- 179966
+ 179861
4
15
- 70676
+ 70634
@@ -22911,22 +22746,22 @@
1
2
- 694852
+ 694445
2
3
- 393404
+ 393173
3
4
- 150970
+ 150882
4
9
- 79167
+ 79120
@@ -23074,32 +22909,32 @@
1
2
- 186978
+ 186868
2
3
- 44850
+ 44824
3
5
- 23218
+ 23204
5
16
- 23535
+ 23521
16
107
- 23006
+ 22993
108
955
- 3452
+ 3450
@@ -23115,17 +22950,17 @@
1
2
- 274918
+ 274756
2
4
- 26001
+ 25986
4
17
- 4122
+ 4119
@@ -23135,11 +22970,11 @@
function_template_argument_value
- 362998
+ 362786
function_id
- 181340
+ 181234
index
@@ -23147,7 +22982,7 @@
arg_value
- 360356
+ 360145
@@ -23161,12 +22996,12 @@
1
2
- 171969
+ 171868
2
8
- 9371
+ 9366
@@ -23182,17 +23017,17 @@
1
2
- 151428
+ 151339
2
3
- 20681
+ 20669
3
97
- 9230
+ 9225
@@ -23330,12 +23165,12 @@
1
2
- 357714
+ 357504
2
3
- 2642
+ 2640
@@ -23351,7 +23186,7 @@
1
2
- 360356
+ 360145
@@ -23361,26 +23196,26 @@
is_variable_template
- 42082
+ 47326
id
- 42082
+ 47326
variable_instantiation
- 49201
+ 258309
to
- 49201
+ 258309
from
- 25019
+ 26281
@@ -23394,7 +23229,7 @@
1
2
- 49201
+ 258309
@@ -23410,22 +23245,42 @@
1
2
- 14236
+ 11203
2
3
- 7746
+ 3559
3
+ 4
+ 1675
+
+
+ 4
+ 6
+ 1884
+
+
+ 6
8
- 1988
+ 1884
8
14
- 1046
+ 2408
+
+
+ 15
+ 26
+ 1989
+
+
+ 32
+ 371
+ 1675
@@ -23435,11 +23290,11 @@
variable_template_argument
- 329648
+ 448035
variable_id
- 26380
+ 247943
index
@@ -23447,7 +23302,7 @@
arg_type
- 217532
+ 217578
@@ -23461,22 +23316,22 @@
1
2
- 16121
+ 119469
2
3
- 5652
+ 99889
3
4
- 3873
+ 18218
4
17
- 732
+ 10365
@@ -23492,52 +23347,22 @@
1
2
- 5757
+ 129206
2
3
- 5234
+ 91408
3
- 4
- 2093
-
-
- 4
5
- 1256
+ 22825
5
- 6
- 2407
-
-
- 6
- 8
- 2303
-
-
- 8
- 11
- 2198
-
-
- 11
- 18
- 2303
-
-
- 18
- 50
- 1988
-
-
- 66
- 516
- 837
+ 17
+ 4502
@@ -23551,43 +23376,53 @@
12
- 1
- 2
+ 11
+ 12
104
- 2
- 3
+ 22
+ 23
628
- 3
- 4
- 418
-
-
- 5
- 6
- 209
-
-
- 27
- 28
+ 29
+ 30
104
- 42
- 43
+ 30
+ 31
+ 314
+
+
+ 44
+ 45
104
- 79
- 80
+ 93
+ 94
104
- 248
- 249
+ 222
+ 223
+ 104
+
+
+ 588
+ 589
+ 104
+
+
+ 1090
+ 1091
+ 104
+
+
+ 1974
+ 1975
104
@@ -23665,17 +23500,22 @@
1
2
- 180579
+ 171403
2
3
- 21878
+ 25024
3
- 38
- 15074
+ 8
+ 17067
+
+
+ 8
+ 94
+ 4083
@@ -23691,12 +23531,12 @@
1
2
- 200259
+ 200302
2
5
- 16854
+ 16857
5
@@ -23711,11 +23551,11 @@
variable_template_argument_value
- 15597
+ 15810
variable_id
- 2826
+ 6596
index
@@ -23723,7 +23563,7 @@
arg_value
- 12038
+ 12041
@@ -23737,12 +23577,12 @@
1
2
- 2617
+ 5968
2
3
- 209
+ 628
@@ -23756,45 +23596,25 @@
12
- 2
- 3
- 418
+ 1
+ 2
+ 314
- 3
- 4
- 104
+ 2
+ 3
+ 5235
4
5
- 1360
-
-
- 5
- 6
- 209
-
-
- 6
- 7
- 209
+ 837
8
9
209
-
- 12
- 17
- 209
-
-
- 20
- 21
- 104
-
@@ -23806,24 +23626,24 @@
12
-
- 2
- 3
- 104
-
6
7
104
- 8
- 9
+ 19
+ 20
104
- 13
- 14
+ 20
+ 21
+ 104
+
+
+ 24
+ 25
104
@@ -23871,12 +23691,12 @@
1
2
- 8479
+ 8271
2
3
- 3559
+ 3769
@@ -23892,7 +23712,7 @@
1
2
- 12038
+ 12041
@@ -23902,15 +23722,15 @@
routinetypes
- 546982
+ 546661
id
- 546982
+ 546661
return_type
- 285945
+ 285778
@@ -23924,7 +23744,7 @@
1
2
- 546982
+ 546661
@@ -23940,17 +23760,17 @@
1
2
- 249233
+ 249087
2
3
- 21315
+ 21303
3
3594
- 15396
+ 15387
@@ -23960,11 +23780,11 @@
routinetypeargs
- 993519
+ 993571
routine
- 429019
+ 429042
index
@@ -23972,7 +23792,7 @@
type_id
- 229563
+ 229575
@@ -23986,27 +23806,27 @@
1
2
- 155707
+ 155715
2
3
- 135479
+ 135486
3
4
- 63976
+ 63979
4
5
- 46100
+ 46103
5
18
- 27754
+ 27756
@@ -24022,27 +23842,27 @@
1
2
- 185814
+ 185824
2
3
- 135009
+ 135016
3
4
- 59272
+ 59275
4
5
- 33869
+ 33871
5
11
- 15053
+ 15054
@@ -24200,27 +24020,27 @@
1
2
- 148651
+ 148659
2
3
- 31047
+ 31049
3
5
- 16934
+ 16935
5
12
- 18346
+ 18347
12
113
- 14582
+ 14583
@@ -24236,17 +24056,17 @@
1
2
- 174994
+ 175004
2
3
- 31047
+ 31049
3
6
- 18816
+ 18817
6
@@ -24261,19 +24081,19 @@
ptrtomembers
- 38103
+ 38105
id
- 38103
+ 38105
type_id
- 38103
+ 38105
class_id
- 15523
+ 15524
@@ -24287,7 +24107,7 @@
1
2
- 38103
+ 38105
@@ -24303,7 +24123,7 @@
1
2
- 38103
+ 38105
@@ -24319,7 +24139,7 @@
1
2
- 38103
+ 38105
@@ -24335,7 +24155,7 @@
1
2
- 38103
+ 38105
@@ -24397,15 +24217,15 @@
specifiers
- 24932
+ 24933
id
- 24932
+ 24933
str
- 24932
+ 24933
@@ -24419,7 +24239,7 @@
1
2
- 24932
+ 24933
@@ -24435,7 +24255,7 @@
1
2
- 24932
+ 24933
@@ -24445,11 +24265,11 @@
typespecifiers
- 1317166
+ 1317234
type_id
- 1298819
+ 1298887
spec_id
@@ -24467,12 +24287,12 @@
1
2
- 1280473
+ 1280540
2
3
- 18346
+ 18347
@@ -24533,11 +24353,11 @@
funspecifiers
- 13049498
+ 13041848
func_id
- 3975160
+ 3972829
spec_id
@@ -24555,27 +24375,27 @@
1
2
- 314977
+ 314792
2
3
- 544551
+ 544231
3
4
- 1145438
+ 1144767
4
5
- 1732127
+ 1731112
5
8
- 238064
+ 237925
@@ -24691,11 +24511,11 @@
varspecifiers
- 2347848
+ 2347970
var_id
- 1255071
+ 1255136
spec_id
@@ -24713,22 +24533,22 @@
1
2
- 735731
+ 735769
2
3
- 203219
+ 203230
3
4
- 58802
+ 58805
4
5
- 257317
+ 257331
@@ -24789,11 +24609,11 @@
attributes
- 696354
+ 696502
id
- 696354
+ 696502
kind
@@ -24801,7 +24621,7 @@
name
- 1674
+ 1675
name_space
@@ -24809,7 +24629,7 @@
location
- 483847
+ 483949
@@ -24823,7 +24643,7 @@
1
2
- 696354
+ 696502
@@ -24839,7 +24659,7 @@
1
2
- 696354
+ 696502
@@ -24855,7 +24675,7 @@
1
2
- 696354
+ 696502
@@ -24871,7 +24691,7 @@
1
2
- 696354
+ 696502
@@ -25088,7 +24908,7 @@
1
2
- 1674
+ 1675
@@ -25264,17 +25084,17 @@
1
2
- 442497
+ 442591
2
9
- 36848
+ 36856
9
201
- 4501
+ 4502
@@ -25290,7 +25110,7 @@
1
2
- 483847
+ 483949
@@ -25306,7 +25126,7 @@
1
2
- 479555
+ 479656
2
@@ -25327,7 +25147,7 @@
1
2
- 483847
+ 483949
@@ -25337,11 +25157,11 @@
attribute_args
- 352341
+ 352360
id
- 352341
+ 352360
kind
@@ -25349,7 +25169,7 @@
attribute
- 270489
+ 270503
index
@@ -25357,7 +25177,7 @@
location
- 329291
+ 329308
@@ -25371,7 +25191,7 @@
1
2
- 352341
+ 352360
@@ -25387,7 +25207,7 @@
1
2
- 352341
+ 352360
@@ -25403,7 +25223,7 @@
1
2
- 352341
+ 352360
@@ -25419,7 +25239,7 @@
1
2
- 352341
+ 352360
@@ -25534,12 +25354,12 @@
1
2
- 204631
+ 204641
2
3
- 49864
+ 49866
3
@@ -25560,7 +25380,7 @@
1
2
- 260140
+ 260153
2
@@ -25581,12 +25401,12 @@
1
2
- 204631
+ 204641
2
3
- 49864
+ 49866
3
@@ -25607,12 +25427,12 @@
1
2
- 204631
+ 204641
2
3
- 49864
+ 49866
3
@@ -25732,12 +25552,12 @@
1
2
- 315179
+ 315195
2
16
- 14112
+ 14113
@@ -25753,7 +25573,7 @@
1
2
- 316590
+ 316606
2
@@ -25774,12 +25594,12 @@
1
2
- 315179
+ 315195
2
16
- 14112
+ 14113
@@ -25795,7 +25615,7 @@
1
2
- 329291
+ 329308
@@ -25805,15 +25625,15 @@
attribute_arg_value
- 351871
+ 351889
arg
- 351871
+ 351889
value
- 34810
+ 34812
@@ -25827,7 +25647,7 @@
1
2
- 351871
+ 351889
@@ -25843,12 +25663,12 @@
1
2
- 16934
+ 16935
2
3
- 12230
+ 12231
3
@@ -25969,15 +25789,15 @@
typeattributes
- 62496
+ 62509
type_id
- 62077
+ 62090
spec_id
- 62496
+ 62509
@@ -25991,7 +25811,7 @@
1
2
- 61658
+ 61671
2
@@ -26012,7 +25832,7 @@
1
2
- 62496
+ 62509
@@ -26022,15 +25842,15 @@
funcattributes
- 635532
+ 635565
func_id
- 447366
+ 447389
spec_id
- 635532
+ 635565
@@ -26044,17 +25864,17 @@
1
2
- 341522
+ 341540
2
3
- 64917
+ 64920
3
6
- 39985
+ 39987
6
@@ -26075,7 +25895,7 @@
1
2
- 635532
+ 635565
@@ -26143,15 +25963,15 @@
stmtattributes
- 1006
+ 1005
stmt_id
- 1006
+ 1005
spec_id
- 1006
+ 1005
@@ -26165,7 +25985,7 @@
1
2
- 1006
+ 1005
@@ -26181,7 +26001,7 @@
1
2
- 1006
+ 1005
@@ -26191,15 +26011,15 @@
unspecifiedtype
- 10352924
+ 10353463
type_id
- 10352924
+ 10353463
unspecified_type_id
- 6956047
+ 6956409
@@ -26213,7 +26033,7 @@
1
2
- 10352924
+ 10353463
@@ -26229,17 +26049,17 @@
1
2
- 4675939
+ 4676182
2
3
- 2037843
+ 2037950
3
147
- 242264
+ 242277
@@ -26249,19 +26069,19 @@
member
- 5134480
+ 5131470
parent
- 689567
+ 689163
index
- 8808
+ 8802
child
- 5070710
+ 5067737
@@ -26275,42 +26095,42 @@
1
3
- 18884
+ 18873
3
4
- 390691
+ 390462
4
5
- 39072
+ 39049
5
7
- 53059
+ 53028
7
10
- 52848
+ 52817
10
16
- 57569
+ 57535
16
30
- 52954
+ 52923
30
251
- 24486
+ 24472
@@ -26326,42 +26146,42 @@
1
3
- 18884
+ 18873
3
4
- 390656
+ 390427
4
5
- 39107
+ 39084
5
7
- 53165
+ 53134
7
10
- 53165
+ 53134
10
16
- 57323
+ 57289
16
29
- 52742
+ 52711
29
253
- 24521
+ 24507
@@ -26377,17 +26197,17 @@
1
2
- 1409
+ 1408
2
3
- 810
+ 809
3
4
- 951
+ 950
5
@@ -26448,7 +26268,7 @@
1
2
- 810
+ 809
2
@@ -26458,7 +26278,7 @@
3
4
- 1162
+ 1161
4
@@ -26503,7 +26323,7 @@
2770
19253
- 458
+ 457
@@ -26519,7 +26339,7 @@
1
2
- 5070710
+ 5067737
@@ -26535,12 +26355,12 @@
1
2
- 5008419
+ 5005483
2
8
- 62290
+ 62254
@@ -26550,15 +26370,15 @@
enclosingfunction
- 121743
+ 121719
child
- 121743
+ 121719
parent
- 69504
+ 69490
@@ -26572,7 +26392,7 @@
1
2
- 121743
+ 121719
@@ -26588,22 +26408,22 @@
1
2
- 36695
+ 36687
2
3
- 21591
+ 21587
3
4
- 6106
+ 6105
4
45
- 5111
+ 5110
@@ -26613,15 +26433,15 @@
derivations
- 402388
+ 402152
derivation
- 402388
+ 402152
sub
- 381883
+ 381659
index
@@ -26629,11 +26449,11 @@
super
- 206461
+ 206340
location
- 38156
+ 38134
@@ -26647,7 +26467,7 @@
1
2
- 402388
+ 402152
@@ -26663,7 +26483,7 @@
1
2
- 402388
+ 402152
@@ -26679,7 +26499,7 @@
1
2
- 402388
+ 402152
@@ -26695,7 +26515,7 @@
1
2
- 402388
+ 402152
@@ -26711,12 +26531,12 @@
1
2
- 366733
+ 366518
2
7
- 15149
+ 15141
@@ -26732,12 +26552,12 @@
1
2
- 366733
+ 366518
2
7
- 15149
+ 15141
@@ -26753,12 +26573,12 @@
1
2
- 366733
+ 366518
2
7
- 15149
+ 15141
@@ -26774,12 +26594,12 @@
1
2
- 366733
+ 366518
2
7
- 15149
+ 15141
@@ -26924,12 +26744,12 @@
1
2
- 199133
+ 199016
2
1225
- 7328
+ 7324
@@ -26945,12 +26765,12 @@
1
2
- 199133
+ 199016
2
1225
- 7328
+ 7324
@@ -26966,12 +26786,12 @@
1
2
- 206003
+ 205882
2
4
- 458
+ 457
@@ -26987,12 +26807,12 @@
1
2
- 202867
+ 202748
2
108
- 3593
+ 3591
@@ -27008,22 +26828,22 @@
1
2
- 28326
+ 28310
2
5
- 3135
+ 3133
5
16
- 2994
+ 2992
17
133
- 2994
+ 2992
142
@@ -27044,22 +26864,22 @@
1
2
- 28326
+ 28310
2
5
- 3135
+ 3133
5
16
- 2994
+ 2992
17
133
- 2994
+ 2992
142
@@ -27080,7 +26900,7 @@
1
2
- 38156
+ 38134
@@ -27096,22 +26916,22 @@
1
2
- 30757
+ 30739
2
5
- 3417
+ 3415
5
55
- 2889
+ 2887
60
420
- 1092
+ 1091
@@ -27121,11 +26941,11 @@
derspecifiers
- 404291
+ 404054
der_id
- 402001
+ 401765
spec_id
@@ -27143,12 +26963,12 @@
1
2
- 399710
+ 399476
2
3
- 2290
+ 2288
@@ -27189,11 +27009,11 @@
direct_base_offsets
- 373110
+ 372891
der_id
- 373110
+ 372891
offset
@@ -27211,7 +27031,7 @@
1
2
- 373110
+ 372891
@@ -27262,11 +27082,11 @@
virtual_base_offsets
- 6661
+ 6660
sub
- 3677
+ 3676
super
@@ -27288,7 +27108,7 @@
1
2
- 2891
+ 2890
2
@@ -27319,7 +27139,7 @@
1
2
- 3099
+ 3098
2
@@ -27553,23 +27373,23 @@
frienddecls
- 715075
+ 714656
id
- 715075
+ 714656
type_id
- 42384
+ 42359
decl_id
- 70182
+ 70141
location
- 6341
+ 6338
@@ -27583,7 +27403,7 @@
1
2
- 715075
+ 714656
@@ -27599,7 +27419,7 @@
1
2
- 715075
+ 714656
@@ -27615,7 +27435,7 @@
1
2
- 715075
+ 714656
@@ -27631,47 +27451,47 @@
1
2
- 6200
+ 6197
2
3
- 13212
+ 13204
3
6
- 2959
+ 2957
6
10
- 3206
+ 3204
10
17
- 3276
+ 3274
17
24
- 3347
+ 3345
25
36
- 3311
+ 3309
37
55
- 3241
+ 3239
55
103
- 3628
+ 3626
@@ -27687,47 +27507,47 @@
1
2
- 6200
+ 6197
2
3
- 13212
+ 13204
3
6
- 2959
+ 2957
6
10
- 3206
+ 3204
10
17
- 3276
+ 3274
17
24
- 3347
+ 3345
25
36
- 3311
+ 3309
37
55
- 3241
+ 3239
55
103
- 3628
+ 3626
@@ -27743,12 +27563,12 @@
1
2
- 40939
+ 40915
2
13
- 1444
+ 1443
@@ -27764,37 +27584,37 @@
1
2
- 40481
+ 40458
2
3
- 5883
+ 5880
3
8
- 6024
+ 6021
8
15
- 5425
+ 5422
15
32
- 5284
+ 5281
32
71
- 5284
+ 5281
72
160
- 1796
+ 1795
@@ -27810,37 +27630,37 @@
1
2
- 40481
+ 40458
2
3
- 5883
+ 5880
3
8
- 6024
+ 6021
8
15
- 5425
+ 5422
15
32
- 5284
+ 5281
32
71
- 5284
+ 5281
72
160
- 1796
+ 1795
@@ -27856,7 +27676,7 @@
1
2
- 69513
+ 69472
2
@@ -27877,7 +27697,7 @@
1
2
- 5954
+ 5950
2
@@ -27898,7 +27718,7 @@
1
2
- 6200
+ 6197
2
@@ -27919,7 +27739,7 @@
1
2
- 5989
+ 5985
2
@@ -27934,19 +27754,19 @@
comments
- 8781270
+ 8783134
id
- 8781270
+ 8783134
contents
- 3342962
+ 3343672
location
- 8781270
+ 8783134
@@ -27960,7 +27780,7 @@
1
2
- 8781270
+ 8783134
@@ -27976,7 +27796,7 @@
1
2
- 8781270
+ 8783134
@@ -27992,17 +27812,17 @@
1
2
- 3058223
+ 3058872
2
7
- 251135
+ 251189
7
32784
- 33603
+ 33610
@@ -28018,17 +27838,17 @@
1
2
- 3058223
+ 3058872
2
7
- 251135
+ 251189
7
32784
- 33603
+ 33610
@@ -28044,7 +27864,7 @@
1
2
- 8781270
+ 8783134
@@ -28060,7 +27880,7 @@
1
2
- 8781270
+ 8783134
@@ -28070,15 +27890,15 @@
commentbinding
- 3145674
+ 3145838
id
- 2490384
+ 2490514
element
- 3068526
+ 3068686
@@ -28092,12 +27912,12 @@
1
2
- 2408061
+ 2408187
2
97
- 82322
+ 82327
@@ -28113,12 +27933,12 @@
1
2
- 2991378
+ 2991533
2
3
- 77148
+ 77152
@@ -28128,15 +27948,15 @@
exprconv
- 7003750
+ 7003755
converted
- 7003750
+ 7003755
conversion
- 7003750
+ 7003755
@@ -28150,7 +27970,7 @@
1
2
- 7003750
+ 7003755
@@ -28166,7 +27986,7 @@
1
2
- 7003750
+ 7003755
@@ -28176,22 +27996,22 @@
compgenerated
- 8494343
+ 8493976
id
- 8494343
+ 8493976
synthetic_destructor_call
- 133110
+ 133104
element
- 103484
+ 103479
i
@@ -28199,7 +28019,7 @@
destructor_call
- 117766
+ 117760
@@ -28213,12 +28033,12 @@
1
2
- 85546
+ 85542
2
3
- 11832
+ 11831
3
@@ -28239,12 +28059,12 @@
1
2
- 85546
+ 85542
2
3
- 11832
+ 11831
3
@@ -28417,7 +28237,7 @@
1
2
- 115749
+ 115743
2
@@ -28438,7 +28258,7 @@
1
2
- 117766
+ 117760
@@ -28486,7 +28306,7 @@
1
2
- 8937
+ 8938
2
@@ -28517,15 +28337,15 @@
namespacembrs
- 2463100
+ 2463228
parentid
- 10819
+ 10820
memberid
- 2463100
+ 2463228
@@ -28605,7 +28425,7 @@
1
2
- 2463100
+ 2463228
@@ -28615,11 +28435,11 @@
exprparents
- 14152882
+ 14152891
expr_id
- 14152882
+ 14152891
child_index
@@ -28627,7 +28447,7 @@
parent_id
- 9417999
+ 9418005
@@ -28641,7 +28461,7 @@
1
2
- 14152882
+ 14152891
@@ -28657,7 +28477,7 @@
1
2
- 14152882
+ 14152891
@@ -28775,12 +28595,12 @@
1
2
- 5388939
+ 5388942
2
3
- 3692597
+ 3692599
3
@@ -28801,12 +28621,12 @@
1
2
- 5388939
+ 5388942
2
3
- 3692597
+ 3692599
3
@@ -28821,22 +28641,22 @@
expr_isload
- 4981779
+ 4981688
expr_id
- 4981779
+ 4981688
conversionkinds
- 4220621
+ 4220624
expr_id
- 4220621
+ 4220624
kind
@@ -28854,7 +28674,7 @@
1
2
- 4220621
+ 4220624
@@ -28883,8 +28703,8 @@
1
- 26289
- 26290
+ 26287
+ 26288
1
@@ -28893,8 +28713,8 @@
1
- 4131029
- 4131030
+ 4131034
+ 4131035
1
@@ -28905,11 +28725,11 @@
iscall
- 3078157
+ 3078281
caller
- 3078157
+ 3078281
kind
@@ -28927,7 +28747,7 @@
1
2
- 3078157
+ 3078281
@@ -28951,8 +28771,8 @@
18
- 167025
- 167026
+ 167040
+ 167041
18
@@ -28963,11 +28783,11 @@
numtemplatearguments
- 543303
+ 543548
expr_id
- 543303
+ 543548
num
@@ -28985,7 +28805,7 @@
1
2
- 543303
+ 543548
@@ -29014,8 +28834,8 @@
18
- 29893
- 29894
+ 29908
+ 29909
18
@@ -29074,23 +28894,23 @@
namequalifiers
- 1618961
+ 1618866
id
- 1618961
+ 1618866
qualifiableelement
- 1618961
+ 1618866
qualifyingelement
- 79675
+ 79653
location
- 282719
+ 282705
@@ -29104,7 +28924,7 @@
1
2
- 1618961
+ 1618866
@@ -29120,7 +28940,7 @@
1
2
- 1618961
+ 1618866
@@ -29136,7 +28956,7 @@
1
2
- 1618961
+ 1618866
@@ -29152,7 +28972,7 @@
1
2
- 1618961
+ 1618866
@@ -29168,7 +28988,7 @@
1
2
- 1618961
+ 1618866
@@ -29184,7 +29004,7 @@
1
2
- 1618961
+ 1618866
@@ -29200,12 +29020,12 @@
1
2
- 45546
+ 45526
2
3
- 17163
+ 17162
3
@@ -29236,12 +29056,12 @@
1
2
- 45546
+ 45526
2
3
- 17163
+ 17162
3
@@ -29272,7 +29092,7 @@
1
2
- 49725
+ 49704
2
@@ -29308,32 +29128,32 @@
1
2
- 91742
+ 91737
2
3
- 25646
+ 25644
3
4
- 42179
+ 42177
4
6
- 12895
+ 12894
6
7
- 89869
+ 89865
7
2135
- 20387
+ 20386
@@ -29349,32 +29169,32 @@
1
2
- 91742
+ 91737
2
3
- 25646
+ 25644
3
4
- 42179
+ 42177
4
6
- 12895
+ 12894
6
7
- 89869
+ 89865
7
2135
- 20387
+ 20386
@@ -29390,17 +29210,17 @@
1
2
- 125168
+ 125162
2
3
- 52354
+ 52352
3
4
- 96622
+ 96618
4
@@ -29415,11 +29235,11 @@
varbind
- 6006364
+ 6006368
expr
- 6006364
+ 6006368
var
@@ -29437,7 +29257,7 @@
1
2
- 6006364
+ 6006368
@@ -29508,15 +29328,15 @@
funbind
- 3080553
+ 3080676
expr
- 3076933
+ 3077056
fun
- 514055
+ 514013
@@ -29530,7 +29350,7 @@
1
2
- 3073313
+ 3073437
2
@@ -29551,32 +29371,32 @@
1
2
- 306582
+ 306531
2
3
- 78775
+ 78789
3
4
- 37226
+ 37224
4
7
- 43475
+ 43473
7
38
- 38703
+ 38701
38
4943
- 9293
+ 9292
@@ -29586,11 +29406,11 @@
expr_allocator
- 46541
+ 46514
expr
- 46541
+ 46514
func
@@ -29612,7 +29432,7 @@
1
2
- 46541
+ 46514
@@ -29628,7 +29448,7 @@
1
2
- 46541
+ 46514
@@ -29712,11 +29532,11 @@
expr_deallocator
- 55314
+ 55282
expr
- 55314
+ 55282
func
@@ -29738,7 +29558,7 @@
1
2
- 55314
+ 55282
@@ -29754,7 +29574,7 @@
1
2
- 55314
+ 55282
@@ -29907,15 +29727,15 @@
expr_cond_true
- 654499
+ 654500
cond
- 654499
+ 654500
true
- 654499
+ 654500
@@ -29929,7 +29749,7 @@
1
2
- 654499
+ 654500
@@ -29945,7 +29765,7 @@
1
2
- 654499
+ 654500
@@ -30003,11 +29823,11 @@
values
- 10646146
+ 10646153
id
- 10646146
+ 10646153
str
@@ -30025,7 +29845,7 @@
1
2
- 10646146
+ 10646153
@@ -30071,15 +29891,15 @@
valuetext
- 4756729
+ 4756702
id
- 4756729
+ 4756702
text
- 703924
+ 703921
@@ -30093,7 +29913,7 @@
1
2
- 4756729
+ 4756702
@@ -30114,17 +29934,17 @@
2
3
- 102489
+ 102490
3
7
- 56759
+ 56757
7
425881
- 17147
+ 17145
@@ -30134,15 +29954,15 @@
valuebind
- 11083050
+ 11083057
val
- 10646146
+ 10646153
expr
- 11083050
+ 11083057
@@ -30156,7 +29976,7 @@
1
2
- 10232022
+ 10232029
2
@@ -30177,7 +29997,7 @@
1
2
- 11083050
+ 11083057
@@ -30388,11 +30208,11 @@
bitfield
- 20936
+ 20941
id
- 20936
+ 20941
bits
@@ -30414,7 +30234,7 @@
1
2
- 20936
+ 20941
@@ -30430,7 +30250,7 @@
1
2
- 20936
+ 20941
@@ -30574,23 +30394,23 @@
initialisers
- 1731824
+ 1732353
init
- 1731824
+ 1732353
var
- 717748
+ 721222
expr
- 1731824
+ 1732353
location
- 390436
+ 390438
@@ -30604,7 +30424,7 @@
1
2
- 1731824
+ 1732353
@@ -30620,7 +30440,7 @@
1
2
- 1731824
+ 1732353
@@ -30636,7 +30456,7 @@
1
2
- 1731824
+ 1732353
@@ -30652,22 +30472,17 @@
1
2
- 629392
+ 632463
2
16
- 31625
+ 32109
16
25
- 56686
-
-
- 25
- 112
- 44
+ 56649
@@ -30683,22 +30498,17 @@
1
2
- 629392
+ 632463
2
16
- 31625
+ 32109
16
25
- 56686
-
-
- 25
- 112
- 44
+ 56649
@@ -30714,12 +30524,12 @@
1
2
- 717666
+ 721215
2
- 4
- 81
+ 3
+ 6
@@ -30735,7 +30545,7 @@
1
2
- 1731824
+ 1732353
@@ -30751,7 +30561,7 @@
1
2
- 1731824
+ 1732353
@@ -30767,7 +30577,7 @@
1
2
- 1731824
+ 1732353
@@ -30783,7 +30593,7 @@
1
2
- 317956
+ 317957
2
@@ -30798,7 +30608,7 @@
15
111459
- 17855
+ 17856
@@ -30814,17 +30624,17 @@
1
2
- 340955
+ 340698
2
4
- 35579
+ 35642
4
12738
- 13901
+ 14096
@@ -30840,7 +30650,7 @@
1
2
- 317956
+ 317957
2
@@ -30855,7 +30665,7 @@
15
111459
- 17855
+ 17856
@@ -30876,15 +30686,15 @@
expr_ancestor
- 121674
+ 121668
exp
- 121674
+ 121668
ancestor
- 84880
+ 84876
@@ -30898,7 +30708,7 @@
1
2
- 121674
+ 121668
@@ -30914,12 +30724,12 @@
1
2
- 61377
+ 61374
2
3
- 16785
+ 16784
3
@@ -30939,19 +30749,19 @@
exprs
- 18300140
+ 18300152
id
- 18300140
+ 18300152
kind
- 3382
+ 3380
location
- 3561673
+ 3559831
@@ -30965,7 +30775,7 @@
1
2
- 18300140
+ 18300152
@@ -30981,7 +30791,7 @@
1
2
- 18300140
+ 18300152
@@ -31050,13 +30860,13 @@
281
- 6591
- 63491
+ 6528
+ 63599
281
- 78915
- 109590
+ 79044
+ 109592
70
@@ -31127,11 +30937,11 @@
1051
- 14609
+ 14618
281
- 16974
+ 16981
32757
140
@@ -31149,32 +30959,32 @@
1
2
- 1936933
+ 1935551
2
3
- 816932
+ 816946
3
4
- 247260
+ 247397
4
8
- 280872
+ 280461
8
- 136
- 267131
+ 137
+ 267010
- 136
+ 137
54140
- 12542
+ 12464
@@ -31190,22 +31000,22 @@
1
2
- 2363808
+ 2362352
2
3
- 873691
+ 873426
3
6
- 307261
+ 307151
6
25
- 16911
+ 16901
@@ -31215,15 +31025,15 @@
expr_types
- 18357789
+ 18357798
id
- 18300140
+ 18300152
typeid
- 829282
+ 829243
value_category
@@ -31241,12 +31051,12 @@
1
2
- 18242562
+ 18242577
2
5
- 57577
+ 57574
@@ -31262,7 +31072,7 @@
1
2
- 18300140
+ 18300152
@@ -31278,42 +31088,42 @@
1
2
- 293470
+ 292376
2
3
- 160720
+ 161054
3
4
- 69824
+ 70343
4
5
- 60801
+ 60978
5
7
- 66996
+ 66993
7
12
- 65321
+ 65336
12
35
- 62638
+ 62653
35
- 78674
- 49509
+ 78689
+ 49506
@@ -31329,12 +31139,12 @@
1
2
- 716180
+ 715462
2
3
- 102314
+ 102993
3
@@ -31353,18 +31163,18 @@
12
- 11828
- 11829
+ 11826
+ 11827
18
- 253738
- 253739
+ 253755
+ 253756
18
- 750551
- 750552
+ 750585
+ 750586
18
@@ -31379,18 +31189,18 @@
12
- 1446
- 1447
+ 1484
+ 1485
18
- 11978
- 11979
+ 11980
+ 11981
18
- 39501
- 39502
+ 39499
+ 39500
18
@@ -31401,15 +31211,15 @@
new_allocated_type
- 47598
+ 47571
expr
- 47598
+ 47571
type_id
- 28150
+ 28134
@@ -31423,7 +31233,7 @@
1
2
- 47598
+ 47571
@@ -31439,17 +31249,17 @@
1
2
- 11767
+ 11760
2
3
- 14903
+ 14894
3
19
- 1479
+ 1478
@@ -32069,15 +31879,15 @@
condition_decl_bind
- 38595
+ 38593
expr
- 38595
+ 38593
decl
- 38595
+ 38593
@@ -32091,7 +31901,7 @@
1
2
- 38595
+ 38593
@@ -32107,7 +31917,7 @@
1
2
- 38595
+ 38593
@@ -32117,15 +31927,15 @@
typeid_bind
- 36430
+ 36408
expr
- 36430
+ 36408
type_id
- 16383
+ 16373
@@ -32139,7 +31949,7 @@
1
2
- 36430
+ 36408
@@ -32155,7 +31965,7 @@
1
2
- 15960
+ 15950
3
@@ -32354,11 +32164,11 @@
lambdas
- 21639
+ 21640
expr
- 21639
+ 21640
default_capture
@@ -32380,7 +32190,7 @@
1
2
- 21639
+ 21640
@@ -32396,7 +32206,7 @@
1
2
- 21639
+ 21640
@@ -32470,15 +32280,15 @@
lambda_capture
- 28224
+ 28226
id
- 28224
+ 28226
lambda
- 20698
+ 20699
index
@@ -32486,7 +32296,7 @@
field
- 28224
+ 28226
captured_by_reference
@@ -32512,7 +32322,7 @@
1
2
- 28224
+ 28226
@@ -32528,7 +32338,7 @@
1
2
- 28224
+ 28226
@@ -32544,7 +32354,7 @@
1
2
- 28224
+ 28226
@@ -32560,7 +32370,7 @@
1
2
- 28224
+ 28226
@@ -32576,7 +32386,7 @@
1
2
- 28224
+ 28226
@@ -32592,7 +32402,7 @@
1
2
- 28224
+ 28226
@@ -32608,12 +32418,12 @@
1
2
- 13171
+ 13172
2
3
- 7526
+ 7527
@@ -32629,12 +32439,12 @@
1
2
- 13171
+ 13172
2
3
- 7526
+ 7527
@@ -32650,12 +32460,12 @@
1
2
- 13171
+ 13172
2
3
- 7526
+ 7527
@@ -32671,7 +32481,7 @@
1
2
- 20698
+ 20699
@@ -32687,7 +32497,7 @@
1
2
- 20698
+ 20699
@@ -32703,12 +32513,12 @@
1
2
- 13171
+ 13172
2
3
- 7526
+ 7527
@@ -32840,7 +32650,7 @@
1
2
- 28224
+ 28226
@@ -32856,7 +32666,7 @@
1
2
- 28224
+ 28226
@@ -32872,7 +32682,7 @@
1
2
- 28224
+ 28226
@@ -32888,7 +32698,7 @@
1
2
- 28224
+ 28226
@@ -32904,7 +32714,7 @@
1
2
- 28224
+ 28226
@@ -32920,7 +32730,7 @@
1
2
- 28224
+ 28226
@@ -33349,19 +33159,19 @@
stmts
- 4660299
+ 4661289
id
- 4660299
+ 4661289
kind
- 1988
+ 1989
location
- 2286915
+ 2287401
@@ -33375,7 +33185,7 @@
1
2
- 4660299
+ 4661289
@@ -33391,7 +33201,7 @@
1
2
- 4660299
+ 4661289
@@ -33619,22 +33429,22 @@
1
2
- 1892154
+ 1892555
2
4
- 175972
+ 176010
4
12
- 176182
+ 176219
12
699
- 42606
+ 42615
@@ -33650,12 +33460,12 @@
1
2
- 2229653
+ 2230127
2
8
- 57261
+ 57274
@@ -33953,15 +33763,15 @@
constexpr_if_then
- 52551
+ 52562
constexpr_if_stmt
- 52551
+ 52562
then_id
- 52551
+ 52562
@@ -33975,7 +33785,7 @@
1
2
- 52551
+ 52562
@@ -33991,7 +33801,7 @@
1
2
- 52551
+ 52562
@@ -34001,15 +33811,15 @@
constexpr_if_else
- 30881
+ 30888
constexpr_if_stmt
- 30881
+ 30888
else_id
- 30881
+ 30888
@@ -34023,7 +33833,7 @@
1
2
- 30881
+ 30888
@@ -34039,7 +33849,7 @@
1
2
- 30881
+ 30888
@@ -34049,15 +33859,15 @@
while_body
- 30207
+ 30201
while_stmt
- 30207
+ 30201
body_id
- 30207
+ 30201
@@ -34071,7 +33881,7 @@
1
2
- 30207
+ 30201
@@ -34087,7 +33897,7 @@
1
2
- 30207
+ 30201
@@ -34097,15 +33907,15 @@
do_body
- 148604
+ 148599
do_stmt
- 148604
+ 148599
body_id
- 148604
+ 148599
@@ -34119,7 +33929,7 @@
1
2
- 148604
+ 148599
@@ -34135,7 +33945,7 @@
1
2
- 148604
+ 148599
@@ -34193,7 +34003,7 @@
switch_case
- 191408
+ 191399
switch_stmt
@@ -34205,7 +34015,7 @@
case_id
- 191408
+ 191399
@@ -34361,7 +34171,7 @@
32
33
- 1909
+ 1908
33
@@ -34402,7 +34212,7 @@
32
33
- 1909
+ 1908
33
@@ -34433,7 +34243,7 @@
1
2
- 191408
+ 191399
@@ -34449,7 +34259,7 @@
1
2
- 191408
+ 191399
@@ -34699,19 +34509,19 @@
stmtparents
- 4052305
+ 4052323
id
- 4052305
+ 4052323
index
- 12209
+ 12210
parent
- 1719463
+ 1719470
@@ -34725,7 +34535,7 @@
1
2
- 4052305
+ 4052323
@@ -34741,7 +34551,7 @@
1
2
- 4052305
+ 4052323
@@ -34879,12 +34689,12 @@
1
2
- 987317
+ 987321
2
3
- 372963
+ 372965
3
@@ -34894,7 +34704,7 @@
4
6
- 111241
+ 111242
6
@@ -34920,12 +34730,12 @@
1
2
- 987317
+ 987321
2
3
- 372963
+ 372965
3
@@ -34935,7 +34745,7 @@
4
6
- 111241
+ 111242
6
@@ -34955,11 +34765,11 @@
ishandler
- 59432
+ 59429
block
- 59432
+ 59429
@@ -35528,15 +35338,15 @@
blockscope
- 1438063
+ 1438137
block
- 1438063
+ 1438137
enclosing
- 1321870
+ 1321939
@@ -35550,7 +35360,7 @@
1
2
- 1438063
+ 1438137
@@ -35566,12 +35376,12 @@
1
2
- 1256011
+ 1256077
2
13
- 65858
+ 65861
@@ -35581,19 +35391,19 @@
jumpinfo
- 253995
+ 253987
id
- 253995
+ 253987
str
- 21152
+ 21151
target
- 53046
+ 53044
@@ -35607,7 +35417,7 @@
1
2
- 253995
+ 253987
@@ -35623,7 +35433,7 @@
1
2
- 253995
+ 253987
@@ -35685,7 +35495,7 @@
1
2
- 16717
+ 16716
2
@@ -35721,7 +35531,7 @@
2
3
- 26428
+ 26427
3
@@ -35736,7 +35546,7 @@
5
8
- 4691
+ 4690
8
@@ -35757,7 +35567,7 @@
1
2
- 53046
+ 53044
@@ -35767,19 +35577,19 @@
preprocdirects
- 4431252
+ 4432193
id
- 4431252
+ 4432193
kind
- 1046
+ 1047
location
- 4428739
+ 4429680
@@ -35793,7 +35603,7 @@
1
2
- 4431252
+ 4432193
@@ -35809,7 +35619,7 @@
1
2
- 4431252
+ 4432193
@@ -35947,7 +35757,7 @@
1
2
- 4428635
+ 4429575
25
@@ -35968,7 +35778,7 @@
1
2
- 4428739
+ 4429680
@@ -35978,15 +35788,15 @@
preprocpair
- 1442296
+ 1442371
begin
- 1206147
+ 1206210
elseelifend
- 1442296
+ 1442371
@@ -36000,12 +35810,12 @@
1
2
- 985992
+ 986044
2
3
- 209805
+ 209816
3
@@ -36026,7 +35836,7 @@
1
2
- 1442296
+ 1442371
@@ -36036,41 +35846,41 @@
preproctrue
- 782302
+ 783284
branch
- 782302
+ 783284
preprocfalse
- 327409
+ 326956
branch
- 327409
+ 326956
preproctext
- 3572638
+ 3573396
id
- 3572638
+ 3573396
head
- 2591544
+ 2592094
body
- 1515816
+ 1516138
@@ -36084,7 +35894,7 @@
1
2
- 3572638
+ 3573396
@@ -36100,7 +35910,7 @@
1
2
- 3572638
+ 3573396
@@ -36116,12 +35926,12 @@
1
2
- 2444464
+ 2444983
2
740
- 147080
+ 147111
@@ -36137,12 +35947,12 @@
1
2
- 2529362
+ 2529899
2
5
- 62181
+ 62195
@@ -36158,17 +35968,17 @@
1
2
- 1372191
+ 1372482
2
6
- 113686
+ 113710
6
11572
- 29939
+ 29945
@@ -36184,17 +35994,17 @@
1
2
- 1375227
+ 1375519
2
7
- 114000
+ 114024
7
2959
- 26589
+ 26595
@@ -36204,15 +36014,15 @@
includes
- 315649
+ 315665
id
- 315649
+ 315665
included
- 118074
+ 118080
@@ -36226,7 +36036,7 @@
1
2
- 315649
+ 315665
@@ -36242,12 +36052,12 @@
1
2
- 61624
+ 61627
2
3
- 22109
+ 22110
3
@@ -36262,7 +36072,7 @@
6
14
- 8937
+ 8938
14
@@ -36325,11 +36135,11 @@
link_parent
- 40143068
+ 40119536
element
- 5115983
+ 5112984
link_target
@@ -36347,17 +36157,17 @@
1
2
- 701934
+ 701522
2
9
- 44146
+ 44120
9
10
- 4369903
+ 4367341
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/src/CHANGELOG.md b/cpp/ql/src/CHANGELOG.md
index 62b22e4e950..ae7e4f7151b 100644
--- a/cpp/ql/src/CHANGELOG.md
+++ b/cpp/ql/src/CHANGELOG.md
@@ -1,3 +1,15 @@
+## 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
### Minor Analysis Improvements
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/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..9646d8b3adf 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
*/
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/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/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/released/0.1.4.md b/cpp/ql/src/change-notes/released/0.1.4.md
new file mode 100644
index 00000000000..49899666aec
--- /dev/null
+++ b/cpp/ql/src/change-notes/released/0.1.4.md
@@ -0,0 +1 @@
+## 0.1.4
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 b79d8f9d00a..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.3
+lastReleaseVersion: 0.3.1
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/experimental/Security/CWE/CWE-670/DangerousUseSSL_shutdown.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-670/DangerousUseSSL_shutdown.cpp
new file mode 100644
index 00000000000..291cbc8edca
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-670/DangerousUseSSL_shutdown.cpp
@@ -0,0 +1,11 @@
+...
+SSL_shutdown(ssl);
+SSL_shutdown(ssl); // BAD
+...
+ switch ((ret = SSL_shutdown(ssl))) {
+ case 1:
+ break;
+ case 0:
+ ERR_clear_error();
+ if (-1 != (ret = SSL_shutdown(ssl))) break; // GOOD
+...
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-670/DangerousUseSSL_shutdown.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-670/DangerousUseSSL_shutdown.qhelp
new file mode 100644
index 00000000000..bc3f8fcc875
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-670/DangerousUseSSL_shutdown.qhelp
@@ -0,0 +1,27 @@
+
+
+
+Incorrect closing of the connection leads to the creation of different states for the server and client, which can be exploited by an attacker.
+
+
+
+
+The following example shows the incorrect and correct usage of function SSL_shutdown.
+
+
+
+
+
+
+ CERT Coding Standard:
+ EXP12-C. Do not ignore values returned by functions - SEI CERT C Coding Standard - Confluence.
+
+
+ Openssl.org:
+ SSL_shutdown - shut down a TLS/SSL connection.
+
+
+
+
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-670/DangerousUseSSL_shutdown.ql b/cpp/ql/src/experimental/Security/CWE/CWE-670/DangerousUseSSL_shutdown.ql
new file mode 100644
index 00000000000..d608fd5a7ed
--- /dev/null
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-670/DangerousUseSSL_shutdown.ql
@@ -0,0 +1,33 @@
+/**
+ * @name Dangerous use SSL_shutdown.
+ * @description Incorrect closing of the connection leads to the creation of different states for the server and client, which can be exploited by an attacker.
+ * @kind problem
+ * @id cpp/dangerous-use-of-ssl-shutdown
+ * @problem.severity warning
+ * @precision medium
+ * @tags correctness
+ * security
+ * external/cwe/cwe-670
+ */
+
+import cpp
+import semmle.code.cpp.commons.Exclusions
+import semmle.code.cpp.valuenumbering.GlobalValueNumbering
+
+from FunctionCall fc, FunctionCall fc1
+where
+ fc != fc1 and
+ fc.getASuccessor+() = fc1 and
+ fc.getTarget().hasName("SSL_shutdown") and
+ fc1.getTarget().hasName("SSL_shutdown") and
+ fc1 instanceof ExprInVoidContext and
+ (
+ globalValueNumber(fc.getArgument(0)) = globalValueNumber(fc1.getArgument(0)) or
+ fc.getArgument(0).(VariableAccess).getTarget() = fc1.getArgument(0).(VariableAccess).getTarget()
+ ) and
+ not exists(FunctionCall fctmp |
+ fctmp.getTarget().hasName("SSL_free") and
+ fc.getASuccessor+() = fctmp and
+ fctmp.getASuccessor+() = fc1
+ )
+select fc, "You need to handle the return value SSL_shutdown"
diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml
index d187cfcc8d1..03b90cb3668 100644
--- a/cpp/ql/src/qlpack.yml
+++ b/cpp/ql/src/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/cpp-queries
-version: 0.1.4-dev
+version: 0.3.2-dev
groups:
- cpp
- queries
diff --git a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
index 3891fcf13a1..4b4a31d6950 100644
--- a/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
+++ b/cpp/ql/test/TestUtilities/InlineExpectationsTest.qll
@@ -239,12 +239,24 @@ private string getColumnString(TColumn column) {
/**
* RegEx pattern to match a single expected result, not including the leading `$`. It consists of one or
- * more comma-separated tags containing only letters, digits, `-` and `_` (note that the first character
- * must not be a digit), optionally followed by `=` and the expected value.
+ * more comma-separated tags optionally followed by `=` and the expected value.
+ *
+ * Tags must be only letters, digits, `-` and `_` (note that the first character
+ * must not be a digit), but can contain anything enclosed in a single set of
+ * square brackets.
+ *
+ * Examples:
+ * - `tag`
+ * - `tag=value`
+ * - `tag,tag2=value`
+ * - `tag[foo bar]=value`
+ *
+ * Not allowed:
+ * - `tag[[[foo bar]`
*/
private string expectationPattern() {
exists(string tag, string tags, string value |
- tag = "[A-Za-z-_][A-Za-z-_0-9]*" and
+ tag = "[A-Za-z-_](?:[A-Za-z-_0-9]|\\[[^\\]\\]]*\\])*" and
tags = "((?:" + tag + ")(?:\\s*,\\s*" + tag + ")*)" and
// In Python, we allow both `"` and `'` for strings, as well as the prefixes `bru`.
// For example, `b"foo"`.
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-670/semmle/tests/DangerousUseSSL_shutdown.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-670/semmle/tests/DangerousUseSSL_shutdown.expected
new file mode 100644
index 00000000000..bb3d9157148
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-670/semmle/tests/DangerousUseSSL_shutdown.expected
@@ -0,0 +1,2 @@
+| test.cpp:45:20:45:31 | call to SSL_shutdown | You need to handle the return value SSL_shutdown |
+| test.cpp:61:11:61:22 | call to SSL_shutdown | You need to handle the return value SSL_shutdown |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-670/semmle/tests/DangerousUseSSL_shutdown.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-670/semmle/tests/DangerousUseSSL_shutdown.qlref
new file mode 100644
index 00000000000..0c2096f68ff
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-670/semmle/tests/DangerousUseSSL_shutdown.qlref
@@ -0,0 +1 @@
+experimental/Security/CWE/CWE-670/DangerousUseSSL_shutdown.ql
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-670/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-670/semmle/tests/test.cpp
new file mode 100644
index 00000000000..9ebe1cc10a5
--- /dev/null
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-670/semmle/tests/test.cpp
@@ -0,0 +1,75 @@
+// it's not exact, but it's enough for an example
+typedef int SSL;
+
+
+int SSL_shutdown(SSL *ssl);
+int SSL_get_error(const SSL *ssl, int ret);
+void ERR_clear_error(void);
+void print_error(char *buff,int code);
+
+int gootTest1(SSL *ssl)
+{
+ int ret;
+ switch ((ret = SSL_shutdown(ssl))) {
+ case 1:
+ break;
+ case 0:
+ ERR_clear_error();
+ if ((ret = SSL_shutdown(ssl)) == 1) break; // GOOD
+ default:
+ print_error("error shutdown",
+ SSL_get_error(ssl, ret));
+ return -1;
+ }
+ return 0;
+}
+int gootTest2(SSL *ssl)
+{
+ int ret;
+ switch ((ret = SSL_shutdown(ssl))) {
+ case 1:
+ break;
+ case 0:
+ ERR_clear_error();
+ if (-1 != (ret = SSL_shutdown(ssl))) break; // GOOD
+ default:
+ print_error("error shutdown",
+ SSL_get_error(ssl, ret));
+ return -1;
+ }
+ return 0;
+}
+int badTest1(SSL *ssl)
+{
+ int ret;
+ switch ((ret = SSL_shutdown(ssl))) {
+ case 1:
+ break;
+ case 0:
+ SSL_shutdown(ssl); // BAD
+ break;
+ default:
+ print_error("error shutdown",
+ SSL_get_error(ssl, ret));
+ return -1;
+ }
+ return 0;
+}
+int badTest2(SSL *ssl)
+{
+ int ret;
+ ret = SSL_shutdown(ssl);
+ switch (ret) {
+ case 1:
+ break;
+ case 0:
+ SSL_shutdown(ssl); // BAD
+ break;
+ default:
+ print_error("error shutdown",
+ SSL_get_error(ssl, ret));
+ return -1;
+ }
+ return 0;
+}
+
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/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/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
index 6b3e01b0e77..1c802f3eeec 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected
@@ -199,7 +199,9 @@ postWithInFlow
| example.c:28:22:28:25 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
| example.c:28:23:28:25 | pos [post update] | PostUpdateNode should not be the target of local flow. |
| globals.cpp:5:9:5:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| globals.cpp:9:5:9:19 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| globals.cpp:13:5:13:19 | flowTestGlobal1 [post update] | PostUpdateNode should not be the target of local flow. |
+| globals.cpp:16:12:16:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| globals.cpp:23:5:23:19 | flowTestGlobal2 [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:8:6:8:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| lambdas.cpp:9:6:9:6 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
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/dataflow-tests/test.ql b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.ql
index 63c20affad1..270b313dc28 100644
--- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.ql
+++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.ql
@@ -2,19 +2,17 @@ import TestUtilities.dataflow.FlowTestCommon
module AstTest {
private import semmle.code.cpp.dataflow.DataFlow
+ private import semmle.code.cpp.controlflow.Guards
/**
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
* S in `if (guarded(x)) S`.
*/
// This is tested in `BarrierGuard.cpp`.
- class TestBarrierGuard extends DataFlow::BarrierGuard {
- TestBarrierGuard() { this.(FunctionCall).getTarget().getName() = "guarded" }
-
- override predicate checks(Expr checked, boolean isTrue) {
- checked = this.(FunctionCall).getArgument(0) and
- isTrue = true
- }
+ predicate testBarrierGuard(GuardCondition g, Expr checked, boolean isTrue) {
+ g.(FunctionCall).getTarget().getName() = "guarded" and
+ checked = g.(FunctionCall).getArgument(0) and
+ isTrue = true
}
/** Common data flow configuration to be used by tests. */
@@ -40,29 +38,26 @@ module AstTest {
}
override predicate isBarrier(DataFlow::Node barrier) {
- barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
+ barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") or
+ barrier = DataFlow::BarrierGuard::getABarrierNode()
}
-
- override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
}
}
module IRTest {
private import semmle.code.cpp.ir.dataflow.DataFlow
private import semmle.code.cpp.ir.IR
+ private import semmle.code.cpp.controlflow.IRGuards
/**
* A `BarrierGuard` that stops flow to all occurrences of `x` within statement
* S in `if (guarded(x)) S`.
*/
// This is tested in `BarrierGuard.cpp`.
- class TestBarrierGuard extends DataFlow::BarrierGuard {
- TestBarrierGuard() { this.(CallInstruction).getStaticCallTarget().getName() = "guarded" }
-
- override predicate checksInstr(Instruction checked, boolean isTrue) {
- checked = this.(CallInstruction).getPositionalArgument(0) and
- isTrue = true
- }
+ predicate testBarrierGuard(IRGuardCondition g, Instruction checked, boolean isTrue) {
+ g.(CallInstruction).getStaticCallTarget().getName() = "guarded" and
+ checked = g.(CallInstruction).getPositionalArgument(0) and
+ isTrue = true
}
/** Common data flow configuration to be used by tests. */
@@ -93,10 +88,9 @@ module IRTest {
}
override predicate isBarrier(DataFlow::Node barrier) {
- barrier.asExpr().(VariableAccess).getTarget().hasName("barrier")
+ barrier.asExpr().(VariableAccess).getTarget().hasName("barrier") or
+ barrier = DataFlow::InstructionBarrierGuard::getABarrierNode()
}
-
- override predicate isBarrierGuard(DataFlow::BarrierGuard bg) { bg instanceof TestBarrierGuard }
}
private predicate readsVariable(LoadInstruction load, Variable var) {
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/declarationEntry/declarationEntry/declarationEntry.cpp b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.cpp
index 3bbfc05ec65..45732eb6ca8 100644
--- a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.cpp
+++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.cpp
@@ -33,3 +33,11 @@ public:
myTemplateClass mtc_int;
myTemplateClass mtc_short;
+
+// Class (UserType)
+
+class myClass
+{
+public:
+ int myMemberVariable;
+};
diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.expected b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.expected
index 76f564d1f7b..19c55430e1c 100644
--- a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.expected
+++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/declarationEntry.expected
@@ -27,5 +27,11 @@
| declarationEntry.cpp:31:4:31:19 | myMemberVariable | declarationEntry.cpp:31:4:31:19 | definition of myMemberVariable | 1 | 1 |
| declarationEntry.cpp:34:22:34:28 | mtc_int | declarationEntry.cpp:34:22:34:28 | definition of mtc_int | 1 | 1 |
| declarationEntry.cpp:35:24:35:32 | mtc_short | declarationEntry.cpp:35:24:35:32 | definition of mtc_short | 1 | 1 |
+| declarationEntry.cpp:39:7:39:7 | operator= | declarationEntry.cpp:39:7:39:7 | declaration of operator= | 1 | 1 |
+| declarationEntry.cpp:39:7:39:7 | operator= | declarationEntry.cpp:39:7:39:7 | declaration of operator= | 1 | 1 |
+| declarationEntry.cpp:39:7:39:13 | myClass | declarationEntry.cpp:39:7:39:13 | definition of myClass | 1 | 1 |
+| declarationEntry.cpp:39:7:39:13 | myClass | forwardDeclaration.cpp:1:7:1:13 | declaration of myClass | 1 | 1 |
+| declarationEntry.cpp:42:6:42:21 | myMemberVariable | declarationEntry.cpp:42:6:42:21 | definition of myMemberVariable | 1 | 1 |
+| forwardDeclaration.cpp:3:10:3:19 | myClassPtr | forwardDeclaration.cpp:3:10:3:19 | definition of myClassPtr | 1 | 1 |
| macro.c:2:1:2:3 | foo | macro.c:2:1:2:3 | declaration of foo | 1 | 1 |
| macro.c:4:5:4:8 | main | macro.c:4:5:4:8 | definition of main | 1 | 1 |
diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/fde.expected b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/fde.expected
index 9a544200cae..71c81c7ac82 100644
--- a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/fde.expected
+++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/fde.expected
@@ -10,5 +10,7 @@
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | 0 | |
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | 0 | |
| declarationEntry.cpp:28:7:28:7 | declaration of operator= | | 0 | |
+| declarationEntry.cpp:39:7:39:7 | declaration of operator= | | 0 | |
+| declarationEntry.cpp:39:7:39:7 | declaration of operator= | | 0 | |
| macro.c:2:1:2:3 | declaration of foo | | 2 | c_linkage, static |
| macro.c:4:5:4:8 | definition of main | | 1 | c_linkage |
diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/forwardDeclaration.cpp b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/forwardDeclaration.cpp
new file mode 100644
index 00000000000..1e5e0c15ce0
--- /dev/null
+++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/forwardDeclaration.cpp
@@ -0,0 +1,3 @@
+class myClass;
+
+myClass *myClassPtr;
diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/roundTrip.expected b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/roundTrip.expected
new file mode 100644
index 00000000000..e0ea52ab027
--- /dev/null
+++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/roundTrip.expected
@@ -0,0 +1,45 @@
+| declarationEntry.c:2:6:2:20 | declaration of myFirstFunction | declarationEntry.c:2:6:2:20 | myFirstFunction | yes |
+| declarationEntry.c:4:6:4:21 | definition of mySecondFunction | declarationEntry.c:4:6:4:21 | mySecondFunction | yes |
+| declarationEntry.c:8:6:8:20 | definition of myThirdFunction | declarationEntry.c:8:6:8:20 | myThirdFunction | yes |
+| declarationEntry.c:13:2:13:2 | declaration of myFourthFunction | declarationEntry.c:13:2:13:2 | myFourthFunction | yes |
+| declarationEntry.c:13:2:13:2 | declaration of myFourthFunction | declarationEntry.c:17:6:17:21 | myFourthFunction | yes |
+| declarationEntry.c:14:2:14:2 | declaration of myFifthFunction | declarationEntry.c:14:2:14:2 | myFifthFunction | yes |
+| declarationEntry.c:17:6:17:21 | declaration of myFourthFunction | declarationEntry.c:13:2:13:2 | myFourthFunction | yes |
+| declarationEntry.c:17:6:17:21 | declaration of myFourthFunction | declarationEntry.c:17:6:17:21 | myFourthFunction | yes |
+| declarationEntry.cpp:3:12:3:21 | declaration of myVariable | declarationEntry.cpp:5:5:5:14 | myVariable | yes |
+| declarationEntry.cpp:5:5:5:14 | definition of myVariable | declarationEntry.cpp:5:5:5:14 | myVariable | yes |
+| declarationEntry.cpp:9:6:9:15 | declaration of myFunction | declarationEntry.cpp:11:6:11:15 | myFunction | yes |
+| declarationEntry.cpp:9:21:9:31 | declaration of myParameter | declarationEntry.cpp:11:21:11:31 | myParameter | yes |
+| declarationEntry.cpp:11:6:11:15 | definition of myFunction | declarationEntry.cpp:11:6:11:15 | myFunction | yes |
+| declarationEntry.cpp:11:21:11:31 | definition of myParameter | declarationEntry.cpp:11:21:11:31 | myParameter | yes |
+| declarationEntry.cpp:18:6:18:11 | declaration of myEnum | declarationEntry.cpp:20:6:20:11 | myEnum | yes |
+| declarationEntry.cpp:20:6:20:11 | definition of myEnum | declarationEntry.cpp:20:6:20:11 | myEnum | yes |
+| declarationEntry.cpp:27:20:27:20 | definition of T | declarationEntry.cpp:27:20:27:20 | T | yes |
+| declarationEntry.cpp:28:7:28:7 | declaration of operator= | declarationEntry.cpp:28:7:28:7 | operator= | yes |
+| declarationEntry.cpp:28:7:28:7 | declaration of operator= | declarationEntry.cpp:28:7:28:7 | operator= | yes |
+| declarationEntry.cpp:28:7:28:7 | declaration of operator= | declarationEntry.cpp:28:7:28:7 | operator= | yes |
+| declarationEntry.cpp:28:7:28:7 | declaration of operator= | declarationEntry.cpp:28:7:28:7 | operator= | yes |
+| declarationEntry.cpp:28:7:28:21 | definition of myTemplateClass | declarationEntry.cpp:28:7:28:21 | myTemplateClass | yes |
+| declarationEntry.cpp:31:4:31:19 | definition of myMemberVariable | declarationEntry.cpp:31:4:31:19 | myMemberVariable | yes |
+| declarationEntry.cpp:31:4:31:19 | definition of myMemberVariable | declarationEntry.cpp:31:4:31:19 | myMemberVariable | yes |
+| declarationEntry.cpp:31:4:31:19 | definition of myMemberVariable | declarationEntry.cpp:31:4:31:19 | myMemberVariable | yes |
+| declarationEntry.cpp:34:22:34:28 | definition of mtc_int | declarationEntry.cpp:34:22:34:28 | mtc_int | yes |
+| declarationEntry.cpp:35:24:35:32 | definition of mtc_short | declarationEntry.cpp:35:24:35:32 | mtc_short | yes |
+| declarationEntry.cpp:39:7:39:7 | declaration of operator= | declarationEntry.cpp:39:7:39:7 | operator= | yes |
+| declarationEntry.cpp:39:7:39:7 | declaration of operator= | declarationEntry.cpp:39:7:39:7 | operator= | yes |
+| declarationEntry.cpp:39:7:39:13 | definition of myClass | declarationEntry.cpp:39:7:39:13 | myClass | yes |
+| declarationEntry.cpp:42:6:42:21 | definition of myMemberVariable | declarationEntry.cpp:42:6:42:21 | myMemberVariable | yes |
+| file://:0:0:0:0 | declaration of 1st parameter | file://:0:0:0:0 | (unnamed parameter 0) | yes |
+| file://:0:0:0:0 | declaration of 1st parameter | file://:0:0:0:0 | (unnamed parameter 0) | yes |
+| file://:0:0:0:0 | declaration of 1st parameter | file://:0:0:0:0 | (unnamed parameter 0) | yes |
+| file://:0:0:0:0 | declaration of 1st parameter | file://:0:0:0:0 | (unnamed parameter 0) | yes |
+| file://:0:0:0:0 | declaration of 1st parameter | file://:0:0:0:0 | (unnamed parameter 0) | yes |
+| file://:0:0:0:0 | declaration of 1st parameter | file://:0:0:0:0 | (unnamed parameter 0) | yes |
+| file://:0:0:0:0 | definition of fp_offset | file://:0:0:0:0 | fp_offset | yes |
+| file://:0:0:0:0 | definition of gp_offset | file://:0:0:0:0 | gp_offset | yes |
+| file://:0:0:0:0 | definition of overflow_arg_area | file://:0:0:0:0 | overflow_arg_area | yes |
+| file://:0:0:0:0 | definition of reg_save_area | file://:0:0:0:0 | reg_save_area | yes |
+| forwardDeclaration.cpp:1:7:1:13 | declaration of myClass | declarationEntry.cpp:39:7:39:13 | myClass | yes |
+| forwardDeclaration.cpp:3:10:3:19 | definition of myClassPtr | forwardDeclaration.cpp:3:10:3:19 | myClassPtr | yes |
+| macro.c:2:1:2:3 | declaration of foo | macro.c:2:1:2:3 | foo | yes |
+| macro.c:4:5:4:8 | definition of main | macro.c:4:5:4:8 | main | yes |
diff --git a/cpp/ql/test/library-tests/declarationEntry/declarationEntry/roundTrip.ql b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/roundTrip.ql
new file mode 100644
index 00000000000..24bbc4b7922
--- /dev/null
+++ b/cpp/ql/test/library-tests/declarationEntry/declarationEntry/roundTrip.ql
@@ -0,0 +1,7 @@
+import cpp
+
+from DeclarationEntry de, Declaration d, string canRoundTrip
+where
+ d = de.getDeclaration() and
+ if d.getADeclarationEntry() = de then canRoundTrip = "yes" else canRoundTrip = "no"
+select de, d, canRoundTrip
diff --git a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll
index ccf243386fe..bd77d831cb7 100644
--- a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll
+++ b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll
@@ -12,4 +12,11 @@ predicate locationIsInStandardHeaders(Location loc) {
*
* This predicate excludes functions defined in standard headers.
*/
-predicate shouldDumpFunction(Function func) { not locationIsInStandardHeaders(func.getLocation()) }
+predicate shouldDumpFunction(Declaration decl) {
+ not locationIsInStandardHeaders(decl.getLocation()) and
+ (
+ decl instanceof Function
+ or
+ decl.(GlobalOrNamespaceVariable).hasInitializer()
+ )
+}
diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected
index 31e5b01229c..79887fffc1f 100644
--- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected
+++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency.expected
@@ -23,6 +23,7 @@ invalidOverlap
nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer
thisArgumentIsNonPointer
+nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected
index 31e5b01229c..79887fffc1f 100644
--- a/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected
+++ b/cpp/ql/test/library-tests/ir/ir/aliased_ssa_consistency_unsound.expected
@@ -23,6 +23,7 @@ invalidOverlap
nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer
thisArgumentIsNonPointer
+nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/ir/ir/ir.cpp b/cpp/ql/test/library-tests/ir/ir/ir.cpp
index e85c5f1b505..4c49d95d453 100644
--- a/cpp/ql/test/library-tests/ir/ir/ir.cpp
+++ b/cpp/ql/test/library-tests/ir/ir/ir.cpp
@@ -1816,4 +1816,18 @@ void switch_initialization(int x) {
}
}
+int global_1;
+
+int global_2 = 1;
+
+const int global_3 = 2;
+
+constructor_only global_4(1);
+
+constructor_only global_5 = constructor_only(2);
+
+char *global_string = "global string";
+
+int global_6 = global_2;
+
// semmle-extractor-options: -std=c++17 --clang
diff --git a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected
index 4b531d9c690..3510d24b7c7 100644
--- a/cpp/ql/test/library-tests/ir/ir/operand_locations.expected
+++ b/cpp/ql/test/library-tests/ir/ir/operand_locations.expected
@@ -4743,6 +4743,14 @@
| ir.cpp:1034:6:1034:20 | ChiTotal | total:m1034_2 |
| ir.cpp:1034:6:1034:20 | SideEffect | m1034_3 |
| ir.cpp:1035:15:1035:15 | Address | &:r1035_1 |
+| ir.cpp:1038:6:1038:8 | Address | &:r1038_3 |
+| ir.cpp:1038:6:1038:8 | SideEffect | ~m1038_8 |
+| ir.cpp:1038:12:1038:18 | Address | &:r1038_4 |
+| ir.cpp:1038:12:1038:18 | Address | &:r1038_4 |
+| ir.cpp:1038:12:1038:18 | ChiPartial | partial:m1038_7 |
+| ir.cpp:1038:12:1038:18 | ChiTotal | total:m1038_2 |
+| ir.cpp:1038:12:1038:18 | Load | m1038_5 |
+| ir.cpp:1038:12:1038:18 | StoreValue | r1038_6 |
| ir.cpp:1038:14:1038:14 | Address | &:r1038_5 |
| ir.cpp:1038:14:1038:14 | Address | &:r1038_5 |
| ir.cpp:1038:14:1038:14 | Address | &:r1038_5 |
@@ -8457,6 +8465,45 @@
| ir.cpp:1815:14:1815:15 | Address | &:r1815_1 |
| ir.cpp:1815:14:1815:15 | Load | m1813_4 |
| ir.cpp:1815:14:1815:15 | Right | r1815_2 |
+| ir.cpp:1821:5:1821:12 | Address | &:r1821_3 |
+| ir.cpp:1821:5:1821:12 | SideEffect | ~m1821_6 |
+| ir.cpp:1821:16:1821:16 | ChiPartial | partial:m1821_5 |
+| ir.cpp:1821:16:1821:16 | ChiTotal | total:m1821_2 |
+| ir.cpp:1821:16:1821:16 | StoreValue | r1821_4 |
+| ir.cpp:1825:18:1825:25 | Address | &:r1825_3 |
+| ir.cpp:1825:18:1825:25 | Arg(this) | this:r1825_3 |
+| ir.cpp:1825:18:1825:25 | SideEffect | ~m1825_10 |
+| ir.cpp:1825:27:1825:27 | Arg(0) | 0:r1825_5 |
+| ir.cpp:1825:27:1825:28 | CallTarget | func:r1825_4 |
+| ir.cpp:1825:27:1825:28 | ChiPartial | partial:m1825_7 |
+| ir.cpp:1825:27:1825:28 | ChiPartial | partial:m1825_9 |
+| ir.cpp:1825:27:1825:28 | ChiTotal | total:m1825_2 |
+| ir.cpp:1825:27:1825:28 | ChiTotal | total:m1825_8 |
+| ir.cpp:1825:27:1825:28 | SideEffect | ~m1825_2 |
+| ir.cpp:1827:18:1827:25 | Address | &:r1827_3 |
+| ir.cpp:1827:18:1827:25 | Arg(this) | this:r1827_3 |
+| ir.cpp:1827:18:1827:25 | SideEffect | ~m1827_10 |
+| ir.cpp:1827:28:1827:47 | CallTarget | func:r1827_4 |
+| ir.cpp:1827:28:1827:47 | ChiPartial | partial:m1827_7 |
+| ir.cpp:1827:28:1827:47 | ChiPartial | partial:m1827_9 |
+| ir.cpp:1827:28:1827:47 | ChiTotal | total:m1827_2 |
+| ir.cpp:1827:28:1827:47 | ChiTotal | total:m1827_8 |
+| ir.cpp:1827:28:1827:47 | SideEffect | ~m1827_2 |
+| ir.cpp:1827:46:1827:46 | Arg(0) | 0:r1827_5 |
+| ir.cpp:1829:7:1829:19 | Address | &:r1829_3 |
+| ir.cpp:1829:7:1829:19 | SideEffect | ~m1829_8 |
+| ir.cpp:1829:23:1829:37 | ChiPartial | partial:m1829_7 |
+| ir.cpp:1829:23:1829:37 | ChiTotal | total:m1829_2 |
+| ir.cpp:1829:23:1829:37 | StoreValue | r1829_6 |
+| ir.cpp:1829:23:1829:37 | Unary | r1829_4 |
+| ir.cpp:1829:23:1829:37 | Unary | r1829_5 |
+| ir.cpp:1831:5:1831:12 | Address | &:r1831_3 |
+| ir.cpp:1831:5:1831:12 | SideEffect | ~m1831_7 |
+| ir.cpp:1831:16:1831:23 | Address | &:r1831_4 |
+| ir.cpp:1831:16:1831:23 | ChiPartial | partial:m1831_6 |
+| ir.cpp:1831:16:1831:23 | ChiTotal | total:m1831_2 |
+| ir.cpp:1831:16:1831:23 | Load | ~m1831_2 |
+| ir.cpp:1831:16:1831:23 | StoreValue | r1831_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_5 |
| perf-regression.cpp:6:3:6:5 | Address | &:r6_7 |
@@ -8700,6 +8747,34 @@
| smart_ptr.cpp:47:43:47:63 | SideEffect | ~m47_16 |
| smart_ptr.cpp:47:43:47:63 | Unary | r47_5 |
| smart_ptr.cpp:47:43:47:63 | Unary | r47_6 |
+| struct_init.cpp:9:13:9:25 | Left | r9_3 |
+| struct_init.cpp:9:13:9:25 | Left | r9_3 |
+| struct_init.cpp:9:13:9:25 | SideEffect | ~m11_10 |
+| struct_init.cpp:9:31:12:1 | Right | r9_4 |
+| struct_init.cpp:9:31:12:1 | Right | r9_6 |
+| struct_init.cpp:9:31:12:1 | Unary | r9_5 |
+| struct_init.cpp:9:31:12:1 | Unary | r9_5 |
+| struct_init.cpp:9:31:12:1 | Unary | r9_7 |
+| struct_init.cpp:9:31:12:1 | Unary | r9_7 |
+| struct_init.cpp:10:5:10:21 | Address | &:r10_1 |
+| struct_init.cpp:10:5:10:21 | Address | &:r10_6 |
+| struct_init.cpp:10:7:10:9 | ChiPartial | partial:m10_4 |
+| struct_init.cpp:10:7:10:9 | ChiTotal | total:m9_2 |
+| struct_init.cpp:10:7:10:9 | StoreValue | r10_3 |
+| struct_init.cpp:10:7:10:9 | Unary | r10_2 |
+| struct_init.cpp:10:12:10:19 | ChiPartial | partial:m10_8 |
+| struct_init.cpp:10:12:10:19 | ChiTotal | total:m10_5 |
+| struct_init.cpp:10:12:10:19 | StoreValue | r10_7 |
+| struct_init.cpp:11:5:11:22 | Address | &:r11_1 |
+| struct_init.cpp:11:5:11:22 | Address | &:r11_6 |
+| struct_init.cpp:11:7:11:9 | ChiPartial | partial:m11_4 |
+| struct_init.cpp:11:7:11:9 | ChiTotal | total:m10_9 |
+| struct_init.cpp:11:7:11:9 | StoreValue | r11_3 |
+| struct_init.cpp:11:7:11:9 | Unary | r11_2 |
+| struct_init.cpp:11:12:11:20 | ChiPartial | partial:m11_9 |
+| struct_init.cpp:11:12:11:20 | ChiTotal | total:m11_5 |
+| struct_init.cpp:11:12:11:20 | StoreValue | r11_8 |
+| struct_init.cpp:11:13:11:20 | Unary | r11_7 |
| struct_init.cpp:16:6:16:20 | ChiPartial | partial:m16_3 |
| struct_init.cpp:16:6:16:20 | ChiTotal | total:m16_2 |
| struct_init.cpp:16:6:16:20 | SideEffect | ~m17_5 |
diff --git a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected
index 9575759051e..cc50472385b 100644
--- a/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected
+++ b/cpp/ql/test/library-tests/ir/ir/raw_consistency.expected
@@ -27,6 +27,7 @@ invalidOverlap
nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer
thisArgumentIsNonPointer
+nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
index 17c59485eb9..8f67435f3c1 100644
--- a/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
+++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.expected
@@ -5650,6 +5650,19 @@ ir.cpp:
# 1034| v1034_5(void) = AliasedUse : ~m?
# 1034| v1034_6(void) = ExitFunction :
+# 1038| (lambda [] type at line 1038, col. 12) lam
+# 1038| Block 0
+# 1038| v1038_1(void) = EnterFunction :
+# 1038| mu1038_2(unknown) = AliasedDefinition :
+# 1038| r1038_3(glval) = VariableAddress[lam] :
+# 1038| r1038_4(glval) = VariableAddress[#temp1038:12] :
+# 1038| mu1038_5(decltype([...](...){...})) = Uninitialized[#temp1038:12] : &:r1038_4
+# 1038| r1038_6(decltype([...](...){...})) = Load[#temp1038:12] : &:r1038_4, ~m?
+# 1038| mu1038_7(decltype([...](...){...})) = Store[lam] : &:r1038_3, r1038_6
+# 1038| v1038_8(void) = ReturnVoid :
+# 1038| v1038_9(void) = AliasedUse : ~m?
+# 1038| v1038_10(void) = ExitFunction :
+
# 1038| void (lambda [] type at line 1038, col. 12)::operator()() const
# 1038| Block 0
# 1038| v1038_1(void) = EnterFunction :
@@ -9720,6 +9733,70 @@ ir.cpp:
# 1785| v1785_7(void) = AliasedUse : ~m?
# 1785| v1785_8(void) = ExitFunction :
+# 1821| int global_2
+# 1821| Block 0
+# 1821| v1821_1(void) = EnterFunction :
+# 1821| mu1821_2(unknown) = AliasedDefinition :
+# 1821| r1821_3(glval) = VariableAddress[global_2] :
+# 1821| r1821_4(int) = Constant[1] :
+# 1821| mu1821_5(int) = Store[global_2] : &:r1821_3, r1821_4
+# 1821| v1821_6(void) = ReturnVoid :
+# 1821| v1821_7(void) = AliasedUse : ~m?
+# 1821| v1821_8(void) = ExitFunction :
+
+# 1825| constructor_only global_4
+# 1825| Block 0
+# 1825| v1825_1(void) = EnterFunction :
+# 1825| mu1825_2(unknown) = AliasedDefinition :
+# 1825| r1825_3(glval) = VariableAddress[global_4] :
+# 1825| r1825_4(glval) = FunctionAddress[constructor_only] :
+# 1825| r1825_5(int) = Constant[1] :
+# 1825| v1825_6(void) = Call[constructor_only] : func:r1825_4, this:r1825_3, 0:r1825_5
+# 1825| mu1825_7(unknown) = ^CallSideEffect : ~m?
+# 1825| mu1825_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1825_3
+# 1825| v1825_9(void) = ReturnVoid :
+# 1825| v1825_10(void) = AliasedUse : ~m?
+# 1825| v1825_11(void) = ExitFunction :
+
+# 1827| constructor_only global_5
+# 1827| Block 0
+# 1827| v1827_1(void) = EnterFunction :
+# 1827| mu1827_2(unknown) = AliasedDefinition :
+# 1827| r1827_3(glval) = VariableAddress[global_5] :
+# 1827| r1827_4(glval) = FunctionAddress[constructor_only] :
+# 1827| r1827_5(int) = Constant[2] :
+# 1827| v1827_6(void) = Call[constructor_only] : func:r1827_4, this:r1827_3, 0:r1827_5
+# 1827| mu1827_7(unknown) = ^CallSideEffect : ~m?
+# 1827| mu1827_8(constructor_only) = ^IndirectMayWriteSideEffect[-1] : &:r1827_3
+# 1827| v1827_9(void) = ReturnVoid :
+# 1827| v1827_10(void) = AliasedUse : ~m?
+# 1827| v1827_11(void) = ExitFunction :
+
+# 1829| char* global_string
+# 1829| Block 0
+# 1829| v1829_1(void) = EnterFunction :
+# 1829| mu1829_2(unknown) = AliasedDefinition :
+# 1829| r1829_3(glval) = VariableAddress[global_string] :
+# 1829| r1829_4(glval) = StringConstant["global string"] :
+# 1829| r1829_5(char *) = Convert : r1829_4
+# 1829| r1829_6(char *) = Convert : r1829_5
+# 1829| mu1829_7(char *) = Store[global_string] : &:r1829_3, r1829_6
+# 1829| v1829_8(void) = ReturnVoid :
+# 1829| v1829_9(void) = AliasedUse : ~m?
+# 1829| v1829_10(void) = ExitFunction :
+
+# 1831| int global_6
+# 1831| Block 0
+# 1831| v1831_1(void) = EnterFunction :
+# 1831| mu1831_2(unknown) = AliasedDefinition :
+# 1831| r1831_3(glval) = VariableAddress[global_6] :
+# 1831| r1831_4(glval) = VariableAddress[global_2] :
+# 1831| r1831_5(int) = Load[global_2] : &:r1831_4, ~m?
+# 1831| mu1831_6(int) = Store[global_6] : &:r1831_3, r1831_5
+# 1831| v1831_7(void) = ReturnVoid :
+# 1831| v1831_8(void) = AliasedUse : ~m?
+# 1831| v1831_9(void) = ExitFunction :
+
perf-regression.cpp:
# 6| void Big::Big()
# 6| Block 0
@@ -9941,6 +10018,34 @@ smart_ptr.cpp:
# 28| v28_6(void) = ExitFunction :
struct_init.cpp:
+# 9| Info infos_in_file[]
+# 9| Block 0
+# 9| v9_1(void) = EnterFunction :
+# 9| mu9_2(unknown) = AliasedDefinition :
+# 9| r9_3(glval) = VariableAddress[infos_in_file] :
+# 9| r9_4(int) = Constant[0] :
+# 9| r9_5(glval) = PointerAdd[16] : r9_3, r9_4
+# 10| r10_1(glval) = FieldAddress[name] : r9_5
+# 10| r10_2(glval) = StringConstant["1"] :
+# 10| r10_3(char *) = Convert : r10_2
+# 10| mu10_4(char *) = Store[?] : &:r10_1, r10_3
+# 10| r10_5(glval<..(*)(..)>) = FieldAddress[handler] : r9_5
+# 10| r10_6(..(*)(..)) = FunctionAddress[handler1] :
+# 10| mu10_7(..(*)(..)) = Store[?] : &:r10_5, r10_6
+# 9| r9_6(int) = Constant[1] :
+# 9| r9_7(glval) = PointerAdd[16] : r9_3, r9_6
+# 11| r11_1(glval) = FieldAddress[name] : r9_7
+# 11| r11_2(glval) = StringConstant["3"] :
+# 11| r11_3(char *) = Convert : r11_2
+# 11| mu11_4(char *) = Store[?] : &:r11_1, r11_3
+# 11| r11_5(glval<..(*)(..)>) = FieldAddress[handler] : r9_7
+# 11| r11_6(glval<..()(..)>) = FunctionAddress[handler2] :
+# 11| r11_7(..(*)(..)) = CopyValue : r11_6
+# 11| mu11_8(..(*)(..)) = Store[?] : &:r11_5, r11_7
+# 9| v9_8(void) = ReturnVoid :
+# 9| v9_9(void) = AliasedUse : ~m?
+# 9| v9_10(void) = ExitFunction :
+
# 16| void let_info_escape(Info*)
# 16| Block 0
# 16| v16_1(void) = EnterFunction :
diff --git a/cpp/ql/test/library-tests/ir/ir/raw_ir.ql b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql
index a0ebe4d2bdd..ae37a4a932b 100644
--- a/cpp/ql/test/library-tests/ir/ir/raw_ir.ql
+++ b/cpp/ql/test/library-tests/ir/ir/raw_ir.ql
@@ -7,5 +7,5 @@ private import semmle.code.cpp.ir.implementation.raw.PrintIR
private import PrintConfig
private class PrintConfig extends PrintIRConfiguration {
- override predicate shouldPrintFunction(Function func) { shouldDumpFunction(func) }
+ override predicate shouldPrintFunction(Declaration decl) { shouldDumpFunction(decl) }
}
diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected
index 31e5b01229c..79887fffc1f 100644
--- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected
+++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency.expected
@@ -23,6 +23,7 @@ invalidOverlap
nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer
thisArgumentIsNonPointer
+nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected
index 31e5b01229c..79887fffc1f 100644
--- a/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected
+++ b/cpp/ql/test/library-tests/ir/ir/unaliased_ssa_consistency_unsound.expected
@@ -23,6 +23,7 @@ invalidOverlap
nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer
thisArgumentIsNonPointer
+nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected
index 31e5b01229c..79887fffc1f 100644
--- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected
+++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency.expected
@@ -23,6 +23,7 @@ invalidOverlap
nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer
thisArgumentIsNonPointer
+nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected
index 31e5b01229c..79887fffc1f 100644
--- a/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected
+++ b/cpp/ql/test/library-tests/ir/ssa/aliased_ssa_consistency_unsound.expected
@@ -23,6 +23,7 @@ invalidOverlap
nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer
thisArgumentIsNonPointer
+nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected
index 31e5b01229c..79887fffc1f 100644
--- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected
+++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency.expected
@@ -23,6 +23,7 @@ invalidOverlap
nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer
thisArgumentIsNonPointer
+nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected
index 31e5b01229c..79887fffc1f 100644
--- a/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected
+++ b/cpp/ql/test/library-tests/ir/ssa/unaliased_ssa_consistency_unsound.expected
@@ -23,6 +23,7 @@ invalidOverlap
nonUniqueEnclosingIRFunction
fieldAddressOnNonPointer
thisArgumentIsNonPointer
+nonUniqueIRVariable
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected
index fcfef712b56..665d30605ee 100644
--- a/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected
+++ b/cpp/ql/test/library-tests/syntax-zoo/aliased_ssa_consistency.expected
@@ -98,6 +98,9 @@ thisArgumentIsNonPointer
| pmcallexpr.cpp:8:2:8:15 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() |
| pointer_to_member.cpp:23:5:23:54 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) |
| pointer_to_member.cpp:24:5:24:49 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) |
+nonUniqueIRVariable
+| misc.c:178:22:178:40 | VariableAddress: __PRETTY_FUNCTION__ | Variable address instruction 'VariableAddress: __PRETTY_FUNCTION__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() |
+| misc.c:179:27:179:34 | VariableAddress: __func__ | Variable address instruction 'VariableAddress: __func__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() |
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected
index e37a676565c..044257ed952 100644
--- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected
+++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected
@@ -1622,6 +1622,7 @@ postWithInFlow
| cpp11.cpp:28:21:28:34 | temporary object [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:29:7:29:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:31:5:31:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| cpp11.cpp:36:5:36:14 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:56:14:56:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:56:14:56:15 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| cpp11.cpp:60:15:60:16 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
@@ -2230,6 +2231,8 @@ postWithInFlow
| ltrbinopexpr.c:37:5:37:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| ltrbinopexpr.c:39:5:39:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| ltrbinopexpr.c:40:5:40:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:10:5:10:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:11:5:11:13 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| misc.c:18:5:18:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
| misc.c:19:5:19:5 | i [post update] | PostUpdateNode should not be the target of local flow. |
| misc.c:20:7:20:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
@@ -2289,6 +2292,7 @@ postWithInFlow
| misc.c:200:24:200:27 | args [post update] | PostUpdateNode should not be the target of local flow. |
| misc.c:200:24:200:27 | array to pointer conversion [post update] | PostUpdateNode should not be the target of local flow. |
| misc.c:208:1:208:3 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
+| misc.c:210:5:210:20 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| misc.c:216:3:216:26 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| misc.c:220:3:220:5 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
| misc.c:220:4:220:5 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
diff --git a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected
index 4a4bea025a8..69f6429c080 100644
--- a/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected
+++ b/cpp/ql/test/library-tests/syntax-zoo/raw_consistency.expected
@@ -148,6 +148,9 @@ thisArgumentIsNonPointer
| pmcallexpr.cpp:8:2:8:15 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() |
| pointer_to_member.cpp:23:5:23:54 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) |
| pointer_to_member.cpp:24:5:24:49 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) |
+nonUniqueIRVariable
+| misc.c:178:22:178:40 | VariableAddress: __PRETTY_FUNCTION__ | Variable address instruction 'VariableAddress: __PRETTY_FUNCTION__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() |
+| misc.c:179:27:179:34 | VariableAddress: __func__ | Variable address instruction 'VariableAddress: __func__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() |
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
diff --git a/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected
index 56e9f2e881a..83e97d12a7d 100644
--- a/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected
+++ b/cpp/ql/test/library-tests/syntax-zoo/unaliased_ssa_consistency.expected
@@ -98,6 +98,9 @@ thisArgumentIsNonPointer
| pmcallexpr.cpp:8:2:8:15 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | array_delete.cpp:5:6:5:6 | void f() | void f() |
| pointer_to_member.cpp:23:5:23:54 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) |
| pointer_to_member.cpp:24:5:24:49 | Call: call to expression | Call instruction 'Call: call to expression' has a `this` argument operand that is not an address, in function '$@'. | pointer_to_member.cpp:14:5:14:9 | int usePM(int PM::*) | int usePM(int PM::*) |
+nonUniqueIRVariable
+| misc.c:178:22:178:40 | VariableAddress: __PRETTY_FUNCTION__ | Variable address instruction 'VariableAddress: __PRETTY_FUNCTION__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() |
+| misc.c:179:27:179:34 | VariableAddress: __func__ | Variable address instruction 'VariableAddress: __func__' has no associated variable, in function '$@'. | misc.c:177:6:177:14 | void magicvars() | void magicvars() |
missingCanonicalLanguageType
multipleCanonicalLanguageTypes
missingIRType
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/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected
index 7322e9723ec..b838a13d5af 100644
--- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected
+++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/diff_ir_expr.expected
@@ -1,18 +1,15 @@
| test.cpp:5:3:5:13 | ... = ... | test.cpp:5:3:5:13 | ... = ... | AST only |
| test.cpp:6:3:6:13 | ... = ... | test.cpp:6:3:6:13 | ... = ... | AST only |
| test.cpp:7:3:7:7 | ... = ... | test.cpp:7:3:7:7 | ... = ... | AST only |
-| test.cpp:10:16:10:16 | 1 | test.cpp:10:16:10:16 | 1 | AST only |
| test.cpp:16:3:16:24 | ... = ... | test.cpp:16:3:16:24 | ... = ... | AST only |
| test.cpp:17:3:17:24 | ... = ... | test.cpp:17:3:17:24 | ... = ... | AST only |
| test.cpp:18:3:18:7 | ... = ... | test.cpp:18:3:18:7 | ... = ... | AST only |
-| test.cpp:21:16:21:16 | 2 | test.cpp:21:16:21:16 | 2 | AST only |
| test.cpp:29:3:29:3 | x | test.cpp:31:3:31:3 | x | IR only |
| test.cpp:29:3:29:24 | ... = ... | test.cpp:29:3:29:24 | ... = ... | AST only |
| test.cpp:30:3:30:17 | call to change_global02 | test.cpp:30:3:30:17 | call to change_global02 | AST only |
| test.cpp:31:3:31:3 | x | test.cpp:29:3:29:3 | x | IR only |
| test.cpp:31:3:31:24 | ... = ... | test.cpp:31:3:31:24 | ... = ... | AST only |
| test.cpp:32:3:32:7 | ... = ... | test.cpp:32:3:32:7 | ... = ... | AST only |
-| test.cpp:35:16:35:16 | 3 | test.cpp:35:16:35:16 | 3 | AST only |
| test.cpp:43:3:43:3 | x | test.cpp:45:3:45:3 | x | IR only |
| test.cpp:43:3:43:24 | ... = ... | test.cpp:43:3:43:24 | ... = ... | AST only |
| test.cpp:43:7:43:24 | ... + ... | test.cpp:45:7:45:24 | ... + ... | IR only |
diff --git a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected
index 24dc1c1ab44..88e365023a1 100644
--- a/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected
+++ b/cpp/ql/test/library-tests/valuenumbering/GlobalValueNumbering/ir_gvn.expected
@@ -69,6 +69,23 @@ test.cpp:
# 1| v1_10(void) = AliasedUse : m1_3
# 1| v1_11(void) = ExitFunction :
+# 10| int global01
+# 10| Block 0
+# 10| v10_1(void) = EnterFunction :
+# 10| m10_2(unknown) = AliasedDefinition :
+# 10| valnum = unique
+# 10| r10_3(glval) = VariableAddress[global01] :
+# 10| valnum = unique
+# 10| r10_4(int) = Constant[1] :
+# 10| valnum = m10_5, r10_4
+# 10| m10_5(int) = Store[global01] : &:r10_3, r10_4
+# 10| valnum = m10_5, r10_4
+# 10| m10_6(unknown) = Chi : total:m10_2, partial:m10_5
+# 10| valnum = unique
+# 10| v10_7(void) = ReturnVoid :
+# 10| v10_8(void) = AliasedUse : ~m10_6
+# 10| v10_9(void) = ExitFunction :
+
# 12| void test01(int, int)
# 12| Block 0
# 12| v12_1(void) = EnterFunction :
@@ -151,6 +168,23 @@ test.cpp:
# 12| v12_10(void) = AliasedUse : m12_3
# 12| v12_11(void) = ExitFunction :
+# 21| int global02
+# 21| Block 0
+# 21| v21_1(void) = EnterFunction :
+# 21| m21_2(unknown) = AliasedDefinition :
+# 21| valnum = unique
+# 21| r21_3(glval) = VariableAddress[global02] :
+# 21| valnum = unique
+# 21| r21_4(int) = Constant[2] :
+# 21| valnum = m21_5, r21_4
+# 21| m21_5(int) = Store[global02] : &:r21_3, r21_4
+# 21| valnum = m21_5, r21_4
+# 21| m21_6(unknown) = Chi : total:m21_2, partial:m21_5
+# 21| valnum = unique
+# 21| v21_7(void) = ReturnVoid :
+# 21| v21_8(void) = AliasedUse : ~m21_6
+# 21| v21_9(void) = ExitFunction :
+
# 25| void test02(int, int)
# 25| Block 0
# 25| v25_1(void) = EnterFunction :
@@ -240,6 +274,23 @@ test.cpp:
# 25| v25_10(void) = AliasedUse : ~m30_4
# 25| v25_11(void) = ExitFunction :
+# 35| int global03
+# 35| Block 0
+# 35| v35_1(void) = EnterFunction :
+# 35| m35_2(unknown) = AliasedDefinition :
+# 35| valnum = unique
+# 35| r35_3(glval) = VariableAddress[global03] :
+# 35| valnum = unique
+# 35| r35_4(int) = Constant[3] :
+# 35| valnum = m35_5, r35_4
+# 35| m35_5(int) = Store[global03] : &:r35_3, r35_4
+# 35| valnum = m35_5, r35_4
+# 35| m35_6(unknown) = Chi : total:m35_2, partial:m35_5
+# 35| valnum = unique
+# 35| v35_7(void) = ReturnVoid :
+# 35| v35_8(void) = AliasedUse : ~m35_6
+# 35| v35_9(void) = ExitFunction :
+
# 39| void test03(int, int, int*)
# 39| Block 0
# 39| v39_1(void) = EnterFunction :
diff --git a/cpp/ql/test/library-tests/variables/global/b.c b/cpp/ql/test/library-tests/variables/global/b.c
index 86f23c6aae8..fb34092b820 100644
--- a/cpp/ql/test/library-tests/variables/global/b.c
+++ b/cpp/ql/test/library-tests/variables/global/b.c
@@ -1,3 +1,4 @@
#include "a.h"
+#include "c.h"
diff --git a/cpp/ql/test/library-tests/variables/global/c.c b/cpp/ql/test/library-tests/variables/global/c.c
new file mode 100644
index 00000000000..0db4e3bc3e0
--- /dev/null
+++ b/cpp/ql/test/library-tests/variables/global/c.c
@@ -0,0 +1,12 @@
+
+int js[] = { 1, 2, 3, 4 };
+
+int ks[4] = { 1, 2, 3, 4 };
+
+int ls[4] = { 1, 2, 3, 4 };
+
+int iss[4][2] = { { 1, 2 }, { 3, 4 }, { 1, 2 }, { 3, 4 } };
+
+typedef int int_alias;
+
+int_alias i;
diff --git a/cpp/ql/test/library-tests/variables/global/c.h b/cpp/ql/test/library-tests/variables/global/c.h
new file mode 100644
index 00000000000..bf03da07c8f
--- /dev/null
+++ b/cpp/ql/test/library-tests/variables/global/c.h
@@ -0,0 +1,10 @@
+
+extern int js[];
+
+extern int ks[];
+
+extern int ls[4];
+
+extern int iss[][2];
+
+extern int i;
diff --git a/cpp/ql/test/library-tests/variables/global/d.cpp b/cpp/ql/test/library-tests/variables/global/d.cpp
new file mode 100644
index 00000000000..62ebc3a8b61
--- /dev/null
+++ b/cpp/ql/test/library-tests/variables/global/d.cpp
@@ -0,0 +1,4 @@
+
+namespace aNameSpace {
+ int xs[] = { 1, 2 };
+}
diff --git a/cpp/ql/test/library-tests/variables/global/d.h b/cpp/ql/test/library-tests/variables/global/d.h
new file mode 100644
index 00000000000..7be2f90115e
--- /dev/null
+++ b/cpp/ql/test/library-tests/variables/global/d.h
@@ -0,0 +1,4 @@
+
+namespace aNameSpace {
+ extern int xs[2];
+}
diff --git a/cpp/ql/test/library-tests/variables/global/e.cpp b/cpp/ql/test/library-tests/variables/global/e.cpp
new file mode 100644
index 00000000000..b2943f7fc67
--- /dev/null
+++ b/cpp/ql/test/library-tests/variables/global/e.cpp
@@ -0,0 +1,2 @@
+
+#include "d.h"
diff --git a/cpp/ql/test/library-tests/variables/global/vardecl.expected b/cpp/ql/test/library-tests/variables/global/vardecl.expected
index 34339189e48..4bcb2cc938d 100644
--- a/cpp/ql/test/library-tests/variables/global/vardecl.expected
+++ b/cpp/ql/test/library-tests/variables/global/vardecl.expected
@@ -1,5 +1,17 @@
| a.c:4:5:4:6 | definition of is | array of {int} | 1 |
| a.h:2:12:2:13 | declaration of is | array of 4 {int} | 1 |
+| c.c:2:5:2:6 | definition of js | array of {int} | 1 |
+| c.c:4:5:4:6 | definition of ks | array of 4 {int} | 1 |
+| c.c:6:5:6:6 | definition of ls | array of 4 {int} | 1 |
+| c.c:8:5:8:7 | definition of iss | array of 4 {array of 2 {int}} | 1 |
+| c.c:12:11:12:11 | definition of i | typedef {int} as "int_alias" | 1 |
+| c.h:2:12:2:13 | declaration of js | array of {int} | 1 |
+| c.h:4:12:4:13 | declaration of ks | array of {int} | 1 |
+| c.h:6:12:6:13 | declaration of ls | array of 4 {int} | 1 |
+| c.h:8:12:8:14 | declaration of iss | array of {array of 2 {int}} | 1 |
+| c.h:10:12:10:12 | declaration of i | int | 1 |
+| d.cpp:3:7:3:8 | definition of xs | array of {int} | 1 |
+| d.h:3:14:3:15 | declaration of xs | array of 2 {int} | 1 |
| file://:0:0:0:0 | definition of fp_offset | unsigned int | 1 |
| file://:0:0:0:0 | definition of gp_offset | unsigned int | 1 |
| file://:0:0:0:0 | definition of overflow_arg_area | pointer to {void} | 1 |
diff --git a/cpp/ql/test/library-tests/variables/global/variables.expected b/cpp/ql/test/library-tests/variables/global/variables.expected
index 01ef82a0dcb..9d022a98264 100644
--- a/cpp/ql/test/library-tests/variables/global/variables.expected
+++ b/cpp/ql/test/library-tests/variables/global/variables.expected
@@ -1,4 +1,12 @@
| a.c:4:5:4:6 | is | array of {int} | 1 |
+| c.c:2:5:2:6 | js | array of {int} | 1 |
+| c.c:4:5:4:6 | ks | array of 4 {int} | 1 |
+| 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 |
+| d.cpp:3:7:3:8 | xs | array of {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 |
| file://:0:0:0:0 | gp_offset | unsigned int | 1 |
| file://:0:0:0:0 | overflow_arg_area | pointer to {void} | 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/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/extractor/Semmle.Extraction.CSharp/Entities/ImplicitMainMethod.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/ImplicitMainMethod.cs
new file mode 100644
index 00000000000..357912bead4
--- /dev/null
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/ImplicitMainMethod.cs
@@ -0,0 +1,36 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Semmle.Extraction.CSharp.Entities.Statements;
+using System.Collections.Generic;
+using System.IO;
+
+namespace Semmle.Extraction.CSharp.Entities
+{
+ internal class ImplicitMainMethod : OrdinaryMethod
+ {
+ private readonly List globalStatements;
+
+ public ImplicitMainMethod(Context cx, IMethodSymbol symbol, List globalStatements)
+ : base(cx, symbol)
+ {
+ this.globalStatements = globalStatements;
+ }
+
+ protected override void PopulateMethodBody(TextWriter trapFile)
+ {
+ GlobalStatementsBlock.Create(Context, this, globalStatements);
+ }
+
+ public static ImplicitMainMethod Create(Context cx, IMethodSymbol method, List globalStatements)
+ {
+ return ImplicitMainMethodFactory.Instance.CreateEntity(cx, method, (method, globalStatements));
+ }
+
+ private class ImplicitMainMethodFactory : CachedEntityFactory<(IMethodSymbol, List), ImplicitMainMethod>
+ {
+ public static ImplicitMainMethodFactory Instance { get; } = new ImplicitMainMethodFactory();
+
+ public override ImplicitMainMethod Create(Context cx, (IMethodSymbol, List) init) => new ImplicitMainMethod(cx, init.Item1, init.Item2);
+ }
+ }
+}
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs
index 8777e95d5d0..cd765159769 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Method.cs
@@ -46,7 +46,7 @@ namespace Semmle.Extraction.CSharp.Entities
// so there's nothing to extract.
}
- private void PopulateMethodBody(TextWriter trapFile)
+ protected virtual void PopulateMethodBody(TextWriter trapFile)
{
if (!IsSourceDeclaration)
return;
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs
index 576eb913433..4de48236707 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/OrdinaryMethod.cs
@@ -8,7 +8,7 @@ namespace Semmle.Extraction.CSharp.Entities
{
internal class OrdinaryMethod : Method
{
- private OrdinaryMethod(Context cx, IMethodSymbol init)
+ protected OrdinaryMethod(Context cx, IMethodSymbol init)
: base(cx, init) { }
public override string Name => Symbol.GetName();
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/GlobalStatementsBlock.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/GlobalStatementsBlock.cs
index 0b31313278a..91301780ed4 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/GlobalStatementsBlock.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Statements/GlobalStatementsBlock.cs
@@ -2,17 +2,22 @@ using Semmle.Extraction.Kinds;
using System.Linq;
using System.IO;
using Semmle.Extraction.Entities;
+using System.Collections.Generic;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System;
namespace Semmle.Extraction.CSharp.Entities.Statements
{
internal class GlobalStatementsBlock : Statement
{
private readonly Method parent;
+ private readonly List globalStatements;
- private GlobalStatementsBlock(Context cx, Method parent)
+ private GlobalStatementsBlock(Context cx, Method parent, List globalStatements)
: base(cx, StmtKind.BLOCK, parent, 0)
{
this.parent = parent;
+ this.globalStatements = globalStatements;
}
public override Microsoft.CodeAnalysis.Location? ReportingLocation
@@ -27,9 +32,9 @@ namespace Semmle.Extraction.CSharp.Entities.Statements
}
}
- public static GlobalStatementsBlock Create(Context cx, Method parent)
+ public static GlobalStatementsBlock Create(Context cx, Method parent, List globalStatements)
{
- var ret = new GlobalStatementsBlock(cx, parent);
+ var ret = new GlobalStatementsBlock(cx, parent, globalStatements);
ret.TryPopulate();
return ret;
}
@@ -37,6 +42,14 @@ namespace Semmle.Extraction.CSharp.Entities.Statements
protected override void PopulateStatement(TextWriter trapFile)
{
trapFile.stmt_location(this, Context.CreateLocation(ReportingLocation));
+
+ for (var i = 0; i < globalStatements.Count; i++)
+ {
+ if (globalStatements[i].Statement is not null)
+ {
+ Statement.Create(Context, globalStatements[i].Statement, this, i);
+ }
+ }
}
}
}
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.CSharp/Populators/CompilationUnitVisitor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs
index be53e48f319..eaa961f672e 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Populators/CompilationUnitVisitor.cs
@@ -3,7 +3,6 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Semmle.Util.Logging;
using Semmle.Extraction.CSharp.Entities;
-using Semmle.Extraction.CSharp.Entities.Statements;
using System.Linq;
namespace Semmle.Extraction.CSharp.Populators
@@ -60,23 +59,14 @@ namespace Semmle.Extraction.CSharp.Populators
}
var entryPoint = Cx.Compilation.GetEntryPoint(System.Threading.CancellationToken.None);
- var entryMethod = Method.Create(Cx, entryPoint);
- if (entryMethod is null)
+ if (entryPoint is null)
{
Cx.ExtractionError("No entry method found. Skipping the extraction of global statements.",
null, Cx.CreateLocation(globalStatements[0].GetLocation()), null, Severity.Info);
return;
}
- var block = GlobalStatementsBlock.Create(Cx, entryMethod);
-
- for (var i = 0; i < globalStatements.Count; i++)
- {
- if (globalStatements[i].Statement is not null)
- {
- Statement.Create(Cx, globalStatements[i].Statement, block, i);
- }
- }
+ ImplicitMainMethod.Create(Cx, entryPoint, globalStatements);
}
}
}
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 8d49a52e30a..0efa6239b0f 100644
--- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
+++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 1.2.2
+
+## 1.2.1
+
+## 1.2.0
+
+## 1.1.4
+
## 1.1.3
## 1.1.2
diff --git a/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.1.4.md b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.1.4.md
new file mode 100644
index 00000000000..651a36ad0f0
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/lib/change-notes/released/1.1.4.md
@@ -0,0 +1 @@
+## 1.1.4
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 35e710ab1bf..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.3
+lastReleaseVersion: 1.2.2
diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml
index 742747fdec9..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.1.4-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 8d49a52e30a..0efa6239b0f 100644
--- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
+++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 1.2.2
+
+## 1.2.1
+
+## 1.2.0
+
+## 1.1.4
+
## 1.1.3
## 1.1.2
diff --git a/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.1.4.md b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.1.4.md
new file mode 100644
index 00000000000..651a36ad0f0
--- /dev/null
+++ b/csharp/ql/campaigns/Solorigate/src/change-notes/released/1.1.4.md
@@ -0,0 +1 @@
+## 1.1.4
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 35e710ab1bf..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.3
+lastReleaseVersion: 1.2.2
diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml
index e5ce4bb150a..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.1.4-dev
+version: 1.2.3-dev
groups:
- csharp
- solorigate
diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md
index 5ef7f456c17..5ea16d73e48 100644
--- a/csharp/ql/lib/CHANGELOG.md
+++ b/csharp/ql/lib/CHANGELOG.md
@@ -1,3 +1,15 @@
+## 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
## 0.2.1
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/csharp/ql/lib/change-notes/released/0.2.3.md b/csharp/ql/lib/change-notes/released/0.2.3.md
new file mode 100644
index 00000000000..b92596ffef1
--- /dev/null
+++ b/csharp/ql/lib/change-notes/released/0.2.3.md
@@ -0,0 +1 @@
+## 0.2.3
diff --git a/csharp/ql/lib/change-notes/released/0.3.0.md b/csharp/ql/lib/change-notes/released/0.3.0.md
new file mode 100644
index 00000000000..54af6e00ac0
--- /dev/null
+++ b/csharp/ql/lib/change-notes/released/0.3.0.md
@@ -0,0 +1,5 @@
+## 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 16a06790aa8..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.2
+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 3654b323743..8f932e28c7a 100644
--- a/csharp/ql/lib/qlpack.yml
+++ b/csharp/ql/lib/qlpack.yml
@@ -1,5 +1,5 @@
name: codeql/csharp-all
-version: 0.2.3-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/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 9e8baa291ca..782d33ea020 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll
@@ -69,6 +69,12 @@
* sources "remote" indicates a default remote flow source, and for summaries
* "taint" indicates a default additional taint step and "value" indicates a
* globally applicable value-preserving step.
+ * 9. The `provenance` column is a tag to indicate the origin of the summary.
+ * There are two supported values: "generated" and "manual". "generated" means that
+ * the model has been emitted by the model generator tool and "manual" means
+ * that the model has been written by hand. This information is used in a heuristic
+ * for dataflow analysis to determine, if a model or source code should be used for
+ * determining flow.
*/
import csharp
@@ -163,17 +169,10 @@ private predicate sinkModel(string row) { any(SinkModelCsv s).row(row) }
private predicate summaryModel(string row) { any(SummaryModelCsv s).row(row) }
-bindingset[input]
-private predicate getKind(string input, string kind, boolean generated) {
- input.splitAt(":", 0) = "generated" and kind = input.splitAt(":", 1) and generated = true
- or
- not input.matches("%:%") and kind = input and generated = false
-}
-
/** Holds if a source model exists for the given parameters. */
predicate sourceModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
- string output, string kind, boolean generated
+ string output, string kind, string provenance
) {
exists(string row |
sourceModel(row) and
@@ -185,14 +184,15 @@ predicate sourceModel(
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = output and
- exists(string k | row.splitAt(";", 7) = k and getKind(k, kind, generated))
+ row.splitAt(";", 7) = kind and
+ row.splitAt(";", 8) = provenance
)
}
/** Holds if a sink model exists for the given parameters. */
predicate sinkModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
- string input, string kind, boolean generated
+ string input, string kind, string provenance
) {
exists(string row |
sinkModel(row) and
@@ -204,14 +204,15 @@ predicate sinkModel(
row.splitAt(";", 4) = signature and
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = input and
- exists(string k | row.splitAt(";", 7) = k and getKind(k, kind, generated))
+ row.splitAt(";", 7) = kind and
+ row.splitAt(";", 8) = provenance
)
}
/** Holds if a summary model exists for the given parameters. */
predicate summaryModel(
string namespace, string type, boolean subtypes, string name, string signature, string ext,
- string input, string output, string kind, boolean generated
+ string input, string output, string kind, string provenance
) {
exists(string row |
summaryModel(row) and
@@ -224,7 +225,8 @@ predicate summaryModel(
row.splitAt(";", 5) = ext and
row.splitAt(";", 6) = input and
row.splitAt(";", 7) = output and
- exists(string k | row.splitAt(";", 8) = k and getKind(k, kind, generated))
+ row.splitAt(";", 8) = kind and
+ row.splitAt(";", 9) = provenance
)
}
@@ -259,25 +261,25 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
part = "source" and
n =
strictcount(string subns, string type, boolean subtypes, string name, string signature,
- string ext, string output, boolean generated |
+ string ext, string output, string provenance |
canonicalNamespaceLink(namespace, subns) and
- sourceModel(subns, type, subtypes, name, signature, ext, output, kind, generated)
+ sourceModel(subns, type, subtypes, name, signature, ext, output, kind, provenance)
)
or
part = "sink" and
n =
strictcount(string subns, string type, boolean subtypes, string name, string signature,
- string ext, string input, boolean generated |
+ string ext, string input, string provenance |
canonicalNamespaceLink(namespace, subns) and
- sinkModel(subns, type, subtypes, name, signature, ext, input, kind, generated)
+ sinkModel(subns, type, subtypes, name, signature, ext, input, kind, provenance)
)
or
part = "summary" and
n =
strictcount(string subns, string type, boolean subtypes, string name, string signature,
- string ext, string input, string output, boolean generated |
+ string ext, string input, string output, string provenance |
canonicalNamespaceLink(namespace, subns) and
- summaryModel(subns, type, subtypes, name, signature, ext, input, output, kind, generated)
+ summaryModel(subns, type, subtypes, name, signature, ext, input, output, kind, provenance)
)
)
}
@@ -286,12 +288,16 @@ predicate modelCoverage(string namespace, int namespaces, string kind, string pa
module CsvValidation {
/** Holds if some row in a CSV-based flow model appears to contain typos. */
query predicate invalidModelRow(string msg) {
- exists(string pred, string namespace, string type, string name, string signature, string ext |
- sourceModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "source"
+ exists(
+ string pred, string namespace, string type, string name, string signature, string ext,
+ string provenance
+ |
+ sourceModel(namespace, type, _, name, signature, ext, _, _, provenance) and pred = "source"
or
- sinkModel(namespace, type, _, name, signature, ext, _, _, _) and pred = "sink"
+ sinkModel(namespace, type, _, name, signature, ext, _, _, provenance) and pred = "sink"
or
- summaryModel(namespace, type, _, name, signature, ext, _, _, _, _) and pred = "summary"
+ summaryModel(namespace, type, _, name, signature, ext, _, _, _, provenance) and
+ pred = "summary"
|
not namespace.regexpMatch("[a-zA-Z0-9_\\.]+") and
msg = "Dubious namespace \"" + namespace + "\" in " + pred + " model."
@@ -307,6 +313,9 @@ module CsvValidation {
or
not ext.regexpMatch("|Attribute") and
msg = "Unrecognized extra API graph element \"" + ext + "\" in " + pred + " model."
+ or
+ not provenance = ["manual", "generated"] and
+ msg = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
)
or
exists(string pred, AccessPath input, string part |
@@ -338,18 +347,18 @@ module CsvValidation {
)
or
exists(string pred, string row, int expect |
- sourceModel(row) and expect = 8 and pred = "source"
+ sourceModel(row) and expect = 9 and pred = "source"
or
- sinkModel(row) and expect = 8 and pred = "sink"
+ sinkModel(row) and expect = 9 and pred = "sink"
or
- summaryModel(row) and expect = 9 and pred = "summary"
+ summaryModel(row) and expect = 10 and pred = "summary"
|
exists(int cols |
cols = 1 + max(int n | exists(row.splitAt(";", n))) and
cols != expect and
msg =
"Wrong number of columns in " + pred + " model row, expected " + expect + ", got " + cols +
- "."
+ " in " + row + "."
)
or
exists(string b |
@@ -359,23 +368,20 @@ module CsvValidation {
)
)
or
- exists(string row, string k, string kind | summaryModel(row) |
- k = row.splitAt(";", 8) and
- getKind(k, kind, _) and
+ exists(string row, string kind | summaryModel(row) |
+ kind = row.splitAt(";", 8) and
not kind = ["taint", "value"] and
msg = "Invalid kind \"" + kind + "\" in summary model."
)
or
- exists(string row, string k, string kind | sinkModel(row) |
- k = row.splitAt(";", 7) and
- getKind(k, kind, _) and
+ exists(string row, string kind | sinkModel(row) |
+ kind = row.splitAt(";", 7) and
not kind = ["code", "sql", "xss", "remote", "html"] and
msg = "Invalid kind \"" + kind + "\" in sink model."
)
or
- exists(string row, string k, string kind | sourceModel(row) |
- k = row.splitAt(";", 7) and
- getKind(k, kind, _) and
+ exists(string row, string kind | sourceModel(row) |
+ kind = row.splitAt(";", 7) and
not kind = "local" and
msg = "Invalid kind \"" + kind + "\" in source model."
)
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 e3602649581..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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 e3602649581..340bfe280b7 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll
@@ -90,14 +90,20 @@ abstract class Configuration extends string {
/** Holds if data flow out of `node` is prohibited. */
predicate isBarrierOut(Node node) { none() }
- /** Holds if data flow through nodes guarded by `guard` is prohibited. */
- predicate isBarrierGuard(BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
+ * Holds if data flow through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isBarrierGuard(BarrierGuard guard) { none() }
/**
+ * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead.
+ *
* Holds if data flow through nodes guarded by `guard` is prohibited when
* the flow state is `state`
*/
- predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
+ deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() }
/**
* Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps.
@@ -335,6 +341,29 @@ private predicate outBarrier(NodeEx node, Configuration config) {
)
}
+/** A bridge class to access the deprecated `isBarrierGuard`. */
+private class BarrierGuardGuardedNodeBridge extends Unit {
+ abstract predicate guardedNode(Node n, Configuration config);
+
+ abstract predicate guardedNode(Node n, FlowState state, Configuration config);
+}
+
+private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge {
+ deprecated override predicate guardedNode(Node n, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g) and
+ n = g.getAGuardedNode()
+ )
+ }
+
+ deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) {
+ exists(BarrierGuard g |
+ config.isBarrierGuard(g, state) and
+ n = g.getAGuardedNode()
+ )
+ }
+}
+
pragma[nomagic]
private predicate fullBarrier(NodeEx node, Configuration config) {
exists(Node n | node.asNode() = n |
@@ -348,10 +377,7 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
not config.isSink(n) and
not config.isSink(n, _)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config)
)
}
@@ -360,10 +386,7 @@ private predicate stateBarrier(NodeEx node, FlowState state, Configuration confi
exists(Node n | node.asNode() = n |
config.isBarrier(n, state)
or
- exists(BarrierGuard g |
- config.isBarrierGuard(g, state) and
- n = g.getAGuardedNode()
- )
+ any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config)
)
}
@@ -405,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
@@ -424,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)
)
@@ -443,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
@@ -458,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
)
@@ -471,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
@@ -484,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
@@ -495,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 |
@@ -539,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)
}
@@ -573,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) {
)
}
-private module Stage1 {
+private module Stage1 implements StageSig {
class ApApprox = Unit;
class Ap = Unit;
@@ -920,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)
}
@@ -1118,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
) {
@@ -1197,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) {
@@ -1211,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
@@ -1224,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]
@@ -1859,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))
}
/**
@@ -1943,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;
@@ -1970,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) {
@@ -2043,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
@@ -2056,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
}
/**
@@ -2620,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
@@ -2804,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;
@@ -2831,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
) {
@@ -2870,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))
@@ -3471,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)
)
}
@@ -3529,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)
)
}
@@ -3643,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) {
@@ -4183,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)
}
/**
@@ -4219,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/DataFlowPublic.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
index 388179eeb8e..7685e519b27 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll
@@ -173,6 +173,33 @@ abstract class NonLocalJumpNode extends Node {
}
/**
+ * Holds if the guard `g` validates the expression `e` upon evaluating to `v`.
+ *
+ * The expression `e` is expected to be a syntactic part of the guard `g`.
+ * For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
+ * the argument `x`.
+ */
+signature predicate guardChecksSig(Guard g, Expr e, AbstractValue v);
+
+/**
+ * Provides a set of barrier nodes for a guard that validates an expression.
+ *
+ * This is expected to be used in `isBarrier`/`isSanitizer` definitions
+ * in data flow and taint tracking.
+ */
+module BarrierGuard {
+ /** Gets a node that is safely guarded by the given guard check. */
+ ExprNode getABarrierNode() {
+ exists(Guard g, Expr e, AbstractValue v |
+ guardChecks(g, e, v) and
+ g.controlsNode(result.getControlFlowNode(), e, v)
+ )
+ }
+}
+
+/**
+ * DEPRECATED: Use `BarrierGuard` module instead.
+ *
* A guard that validates some expression.
*
* To use this in a configuration, extend the class and provide a
@@ -181,7 +208,7 @@ abstract class NonLocalJumpNode extends Node {
*
* It is important that all extending classes in scope are disjoint.
*/
-class BarrierGuard extends Guard {
+deprecated class BarrierGuard extends Guard {
/** Holds if this guard validates `e` upon evaluating to `v`. */
abstract predicate checks(Expr e, AbstractValue v);
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
index 400a10741fb..7abae2b105b 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImpl.qll
@@ -1116,13 +1116,13 @@ module Private {
preservesValue = false and result = "taint"
}
- private string renderGenerated(RelevantSummarizedCallable c) {
- if c.(SummarizedCallable).isAutoGenerated() then result = "generated:" else result = ""
+ private string renderProvenance(RelevantSummarizedCallable c) {
+ if c.(SummarizedCallable).isAutoGenerated() then result = "generated" else result = "manual"
}
/**
* A query predicate for outputting flow summaries in semi-colon separated format in QL tests.
- * The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;(generated:)?kind",
+ * The syntax is: "namespace;type;overrides;name;signature;ext;inputspec;outputspec;kind;provenance"",
* ext is hardcoded to empty.
*/
query predicate summary(string csv) {
@@ -1133,7 +1133,7 @@ module Private {
c.relevantSummary(input, output, preservesValue) and
csv =
c.getCallableCsv() + getComponentStackCsv(input) + ";" + getComponentStackCsv(output) +
- ";" + renderGenerated(c) + renderKind(preservesValue)
+ ";" + renderKind(preservesValue) + ";" + renderProvenance(c)
)
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll
index 7fa9df72ba2..cbd990691a9 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/FlowSummaryImplSpecific.qll
@@ -91,6 +91,13 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
)
}
+bindingset[provenance]
+private boolean isGenerated(string provenance) {
+ provenance = "generated" and result = true
+ or
+ provenance != "generated" and result = false
+}
+
/**
* Holds if an external flow summary exists for `c` with input specification
* `input`, output specification `output`, kind `kind`, and a flag `generated`
@@ -98,9 +105,11 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) {
*/
predicate summaryElement(Callable c, string input, string output, string kind, boolean generated) {
exists(
- string namespace, string type, boolean subtypes, string name, string signature, string ext
+ string namespace, string type, boolean subtypes, string name, string signature, string ext,
+ string provenance
|
- summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, generated) and
+ summaryModel(namespace, type, subtypes, name, signature, ext, input, output, kind, provenance) and
+ generated = isGenerated(provenance) and
c = interpretElement(namespace, type, subtypes, name, signature, ext)
)
}
@@ -112,9 +121,11 @@ predicate summaryElement(Callable c, string input, string output, string kind, b
*/
predicate sourceElement(Element e, string output, string kind, boolean generated) {
exists(
- string namespace, string type, boolean subtypes, string name, string signature, string ext
+ string namespace, string type, boolean subtypes, string name, string signature, string ext,
+ string provenance
|
- sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, generated) and
+ sourceModel(namespace, type, subtypes, name, signature, ext, output, kind, provenance) and
+ generated = isGenerated(provenance) and
e = interpretElement(namespace, type, subtypes, name, signature, ext)
)
}
@@ -126,9 +137,11 @@ predicate sourceElement(Element e, string output, string kind, boolean generated
*/
predicate sinkElement(Element e, string input, string kind, boolean generated) {
exists(
- string namespace, string type, boolean subtypes, string name, string signature, string ext
+ string namespace, string type, boolean subtypes, string name, string signature, string ext,
+ string provenance
|
- sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, generated) and
+ sinkModel(namespace, type, subtypes, name, signature, ext, input, kind, provenance) and
+ generated = isGenerated(provenance) and
e = interpretElement(namespace, type, subtypes, name, signature, ext)
)
}
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 268c5cd49df..f263ee0114b 100755
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll
@@ -19,12 +19,6 @@ private import semmle.code.csharp.frameworks.WCF
*/
predicate defaultTaintSanitizer(DataFlow::Node node) { none() }
-/**
- * Holds if `guard` should be a sanitizer guard in all global taint flow configurations
- * but not in local taint.
- */
-predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
-
/**
* Holds if default `TaintTracking::Configuration`s should allow implicit reads
* of `c` at sinks and inputs to additional taint steps.
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
index 8cf5a49bc0b..e6ce1ada8d4 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking1/TaintTrackingImpl.qll
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
+ * Holds if taint propagation through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
- this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
+ deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ this.isSanitizerGuard(guard)
}
/**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ none()
+ }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ deprecated final override predicate isBarrierGuard(
+ DataFlow::BarrierGuard guard, DataFlow::FlowState state
+ ) {
this.isSanitizerGuard(guard, state)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
index 8cf5a49bc0b..e6ce1ada8d4 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking2/TaintTrackingImpl.qll
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
+ * Holds if taint propagation through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
- this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
+ deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ this.isSanitizerGuard(guard)
}
/**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ none()
+ }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ deprecated final override predicate isBarrierGuard(
+ DataFlow::BarrierGuard guard, DataFlow::FlowState state
+ ) {
this.isSanitizerGuard(guard, state)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
index 8cf5a49bc0b..e6ce1ada8d4 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking3/TaintTrackingImpl.qll
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
+ * Holds if taint propagation through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
- this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
+ deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ this.isSanitizerGuard(guard)
}
/**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ none()
+ }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ deprecated final override predicate isBarrierGuard(
+ DataFlow::BarrierGuard guard, DataFlow::FlowState state
+ ) {
this.isSanitizerGuard(guard, state)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll
index 8cf5a49bc0b..e6ce1ada8d4 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking4/TaintTrackingImpl.qll
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
+ * Holds if taint propagation through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
- this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
+ deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ this.isSanitizerGuard(guard)
}
/**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ none()
+ }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ deprecated final override predicate isBarrierGuard(
+ DataFlow::BarrierGuard guard, DataFlow::FlowState state
+ ) {
this.isSanitizerGuard(guard, state)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll
index 8cf5a49bc0b..e6ce1ada8d4 100644
--- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll
+++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/tainttracking5/TaintTrackingImpl.qll
@@ -116,20 +116,30 @@ abstract class Configuration extends DataFlow::Configuration {
final override predicate isBarrierOut(DataFlow::Node node) { this.isSanitizerOut(node) }
- /** Holds if taint propagation through nodes guarded by `guard` is prohibited. */
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
+ /**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
+ * Holds if taint propagation through nodes guarded by `guard` is prohibited.
+ */
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
- this.isSanitizerGuard(guard) or defaultTaintSanitizerGuard(guard)
+ deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) {
+ this.isSanitizerGuard(guard)
}
/**
+ * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead.
+ *
* Holds if taint propagation through nodes guarded by `guard` is prohibited
* when the flow state is `state`.
*/
- predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { none() }
+ deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ none()
+ }
- final override predicate isBarrierGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) {
+ deprecated final override predicate isBarrierGuard(
+ DataFlow::BarrierGuard guard, DataFlow::FlowState state
+ ) {
this.isSanitizerGuard(guard, state)
}
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll
index e62c6c3ee94..c6559f23d7a 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/EntityFramework.qll
@@ -240,7 +240,7 @@ module EntityFramework {
private class SystemDataEntityDbSetSqlQuerySinkModelCsv extends SinkModelCsv {
override predicate row(string row) {
row =
- "System.Data.Entity;DbSet;false;SqlQuery;(System.String,System.Object[]);;Argument[0];sql"
+ "System.Data.Entity;DbSet;false;SqlQuery;(System.String,System.Object[]);;Argument[0];sql;manual"
}
}
@@ -249,14 +249,14 @@ module EntityFramework {
override predicate row(string row) {
row =
[
- "System.Data.Entity;Database;false;SqlQuery;(System.Type,System.String,System.Object[]);;Argument[1];sql",
- "System.Data.Entity;Database;false;SqlQuery<>;(System.String,System.Object[]);;Argument[0];sql",
- "System.Data.Entity;Database;false;ExecuteSqlCommand;(System.String,System.Object[]);;Argument[0];sql",
- "System.Data.Entity;Database;false;ExecuteSqlCommand;(System.Data.Entity.TransactionalBehavior,System.String,System.Object[]);;Argument[1];sql",
- "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.Data.Entity.TransactionalBehavior,System.String,System.Threading.CancellationToken,System.Object[]);;Argument[1];sql",
- "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.String,System.Threading.CancellationToken,System.Object[]);;Argument[0];sql",
- "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.String,System.Object[]);;Argument[0];sql",
- "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.Data.Entity.TransactionalBehavior,System.String,System.Object[]);;Argument[1];sql"
+ "System.Data.Entity;Database;false;SqlQuery;(System.Type,System.String,System.Object[]);;Argument[1];sql;manual",
+ "System.Data.Entity;Database;false;SqlQuery<>;(System.String,System.Object[]);;Argument[0];sql;manual",
+ "System.Data.Entity;Database;false;ExecuteSqlCommand;(System.String,System.Object[]);;Argument[0];sql;manual",
+ "System.Data.Entity;Database;false;ExecuteSqlCommand;(System.Data.Entity.TransactionalBehavior,System.String,System.Object[]);;Argument[1];sql;manual",
+ "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.Data.Entity.TransactionalBehavior,System.String,System.Threading.CancellationToken,System.Object[]);;Argument[1];sql;manual",
+ "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.String,System.Threading.CancellationToken,System.Object[]);;Argument[0];sql;manual",
+ "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.String,System.Object[]);;Argument[0];sql;manual",
+ "System.Data.Entity;Database;false;ExecuteSqlCommandAsync;(System.Data.Entity.TransactionalBehavior,System.String,System.Object[]);;Argument[1];sql;manual"
]
}
}
@@ -266,7 +266,7 @@ module EntityFramework {
override predicate row(string row) {
row =
[
- "Microsoft.EntityFrameworkCore;RelationalQueryableExtensions;false;FromSqlRaw<>;(Microsoft.EntityFrameworkCore.DbSet,System.String,System.Object[]);;Argument[1];sql",
+ "Microsoft.EntityFrameworkCore;RelationalQueryableExtensions;false;FromSqlRaw<>;(Microsoft.EntityFrameworkCore.DbSet,System.String,System.Object[]);;Argument[1];sql;manual",
]
}
}
@@ -276,11 +276,11 @@ module EntityFramework {
override predicate row(string row) {
row =
[
- "Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;false;ExecuteSqlRaw;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
- "Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;false;ExecuteSqlRaw;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Object[]);;Argument[1];sql",
- "Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;false;ExecuteSqlRawAsync;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Threading.CancellationToken);;Argument[1];sql",
- "Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;false;ExecuteSqlRawAsync;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Object[]);;Argument[1];sql",
- "Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;false;ExecuteSqlRawAsync;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
+ "Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;false;ExecuteSqlRaw;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
+ "Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;false;ExecuteSqlRaw;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Object[]);;Argument[1];sql;manual",
+ "Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;false;ExecuteSqlRawAsync;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;false;ExecuteSqlRawAsync;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Object[]);;Argument[1];sql;manual",
+ "Microsoft.EntityFrameworkCore;RelationalDatabaseFacadeExtensions;false;ExecuteSqlRawAsync;(Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
]
}
}
diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll
index d563de43b96..221c0f0fac6 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/JsonNET.qll
@@ -52,65 +52,65 @@ module JsonNET {
override predicate row(string row) {
row =
[
- "Newtonsoft.Json;JsonConvert;false;DeserializeAnonymousType<>;(System.String,T);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeAnonymousType<>;(System.String,T,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeObject;(System.String);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeObject;(System.String,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeObject;(System.String,System.Type);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeObject;(System.String,System.Type,Newtonsoft.Json.JsonConverter[]);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeObject;(System.String,System.Type,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeObject<>;(System.String);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeObject<>;(System.String,Newtonsoft.Json.JsonConverter[]);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeObject<>;(System.String,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeXNode;(System.String);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeXNode;(System.String,System.String);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeXNode;(System.String,System.String,System.Boolean);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeXNode;(System.String,System.String,System.Boolean,System.Boolean);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeXmlNode;(System.String);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeXmlNode;(System.String,System.String);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeXmlNode;(System.String,System.String,System.Boolean);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;DeserializeXmlNode;(System.String,System.String,System.Boolean,System.Boolean);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;PopulateObject;(System.String,System.Object);;Argument[0];Argument[1];taint",
- "Newtonsoft.Json;JsonConvert;false;PopulateObject;(System.String,System.Object,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];Argument[1];taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,Newtonsoft.Json.Formatting);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonConverter[]);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,Newtonsoft.Json.JsonConverter[]);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,System.Type,Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,System.Type,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeXNode;(System.Xml.Linq.XObject);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeXNode;(System.Xml.Linq.XObject,Newtonsoft.Json.Formatting);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeXNode;(System.Xml.Linq.XObject,Newtonsoft.Json.Formatting,System.Boolean);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeXmlNode;(System.Xml.XmlNode);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeXmlNode;(System.Xml.XmlNode,Newtonsoft.Json.Formatting);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;SerializeXmlNode;(System.Xml.XmlNode,Newtonsoft.Json.Formatting,System.Boolean);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Boolean);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Byte);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Char);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.DateTime);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.DateTime,Newtonsoft.Json.DateFormatHandling,Newtonsoft.Json.DateTimeZoneHandling);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.DateTimeOffset);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.DateTimeOffset,Newtonsoft.Json.DateFormatHandling);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Decimal);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Double);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Enum);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Guid);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Int16);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Int32);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Int64);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Object);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.SByte);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Single);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.String);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.String,System.Char);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.String,System.Char,Newtonsoft.Json.StringEscapeHandling);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.TimeSpan);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.UInt16);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.UInt32);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.UInt64);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonConvert;false;ToString;(System.Uri);;Argument[0];ReturnValue;taint",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeAnonymousType<>;(System.String,T);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeAnonymousType<>;(System.String,T,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeObject;(System.String);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeObject;(System.String,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeObject;(System.String,System.Type);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeObject;(System.String,System.Type,Newtonsoft.Json.JsonConverter[]);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeObject;(System.String,System.Type,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeObject<>;(System.String);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeObject<>;(System.String,Newtonsoft.Json.JsonConverter[]);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeObject<>;(System.String,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeXNode;(System.String);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeXNode;(System.String,System.String);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeXNode;(System.String,System.String,System.Boolean);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeXNode;(System.String,System.String,System.Boolean,System.Boolean);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeXmlNode;(System.String);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeXmlNode;(System.String,System.String);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeXmlNode;(System.String,System.String,System.Boolean);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;DeserializeXmlNode;(System.String,System.String,System.Boolean,System.Boolean);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;PopulateObject;(System.String,System.Object);;Argument[0];Argument[1];taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;PopulateObject;(System.String,System.Object,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];Argument[1];taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,Newtonsoft.Json.Formatting);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonConverter[]);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,Newtonsoft.Json.JsonConverter[]);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,System.Type,Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeObject;(System.Object,System.Type,Newtonsoft.Json.JsonSerializerSettings);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeXNode;(System.Xml.Linq.XObject);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeXNode;(System.Xml.Linq.XObject,Newtonsoft.Json.Formatting);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeXNode;(System.Xml.Linq.XObject,Newtonsoft.Json.Formatting,System.Boolean);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeXmlNode;(System.Xml.XmlNode);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeXmlNode;(System.Xml.XmlNode,Newtonsoft.Json.Formatting);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;SerializeXmlNode;(System.Xml.XmlNode,Newtonsoft.Json.Formatting,System.Boolean);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Boolean);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Byte);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Char);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.DateTime);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.DateTime,Newtonsoft.Json.DateFormatHandling,Newtonsoft.Json.DateTimeZoneHandling);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.DateTimeOffset);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.DateTimeOffset,Newtonsoft.Json.DateFormatHandling);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Decimal);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Double);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Enum);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Guid);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Int16);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Int32);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Int64);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Object);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.SByte);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Single);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.String);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.String,System.Char);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.String,System.Char,Newtonsoft.Json.StringEscapeHandling);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.TimeSpan);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.UInt16);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.UInt32);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.UInt64);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonConvert;false;ToString;(System.Uri);;Argument[0];ReturnValue;taint;manual",
]
}
}
@@ -180,13 +180,13 @@ module JsonNET {
override predicate row(string row) {
row =
[
- "Newtonsoft.Json;JsonSerializer;false;Deserialize;(Newtonsoft.Json.JsonReader);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonSerializer;false;Deserialize;(Newtonsoft.Json.JsonReader,System.Type);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonSerializer;false;Deserialize;(System.IO.TextReader,System.Type);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json;JsonSerializer;false;Serialize;(Newtonsoft.Json.JsonWriter,System.Object);;Argument[1];Argument[0];taint",
- "Newtonsoft.Json;JsonSerializer;false;Serialize;(Newtonsoft.Json.JsonWriter,System.Object,System.Type);;Argument[1];Argument[0];taint",
- "Newtonsoft.Json;JsonSerializer;false;Serialize;(System.IO.TextWriter,System.Object);;Argument[1];Argument[0];taint",
- "Newtonsoft.Json;JsonSerializer;false;Serialize;(System.IO.TextWriter,System.Object,System.Type);;Argument[1];Argument[0];taint"
+ "Newtonsoft.Json;JsonSerializer;false;Deserialize;(Newtonsoft.Json.JsonReader);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonSerializer;false;Deserialize;(Newtonsoft.Json.JsonReader,System.Type);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonSerializer;false;Deserialize;(System.IO.TextReader,System.Type);;Argument[0];ReturnValue;taint;manual",
+ "Newtonsoft.Json;JsonSerializer;false;Serialize;(Newtonsoft.Json.JsonWriter,System.Object);;Argument[1];Argument[0];taint;manual",
+ "Newtonsoft.Json;JsonSerializer;false;Serialize;(Newtonsoft.Json.JsonWriter,System.Object,System.Type);;Argument[1];Argument[0];taint;manual",
+ "Newtonsoft.Json;JsonSerializer;false;Serialize;(System.IO.TextWriter,System.Object);;Argument[1];Argument[0];taint;manual",
+ "Newtonsoft.Json;JsonSerializer;false;Serialize;(System.IO.TextWriter,System.Object,System.Type);;Argument[1];Argument[0];taint;manual"
]
}
}
@@ -228,11 +228,11 @@ module JsonNET {
override predicate row(string row) {
row =
[
- "Newtonsoft.Json.Linq;JToken;false;SelectToken;(System.String);;Argument[Qualifier];ReturnValue;taint",
- "Newtonsoft.Json.Linq;JToken;false;SelectToken;(System.String,Newtonsoft.Json.Linq.JsonSelectSettings);;Argument[Qualifier];ReturnValue;taint",
- "Newtonsoft.Json.Linq;JToken;false;SelectToken;(System.String,System.Boolean);;Argument[Qualifier];ReturnValue;taint",
- "Newtonsoft.Json.Linq;JToken;false;ToString;();;Argument[Qualifier];ReturnValue;taint",
- "Newtonsoft.Json.Linq;JToken;false;ToString;(Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonConverter[]);;Argument[Qualifier];ReturnValue;taint",
+ "Newtonsoft.Json.Linq;JToken;false;SelectToken;(System.String);;Argument[Qualifier];ReturnValue;taint;manual",
+ "Newtonsoft.Json.Linq;JToken;false;SelectToken;(System.String,Newtonsoft.Json.Linq.JsonSelectSettings);;Argument[Qualifier];ReturnValue;taint;manual",
+ "Newtonsoft.Json.Linq;JToken;false;SelectToken;(System.String,System.Boolean);;Argument[Qualifier];ReturnValue;taint;manual",
+ "Newtonsoft.Json.Linq;JToken;false;ToString;();;Argument[Qualifier];ReturnValue;taint;manual",
+ "Newtonsoft.Json.Linq;JToken;false;ToString;(Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonConverter[]);;Argument[Qualifier];ReturnValue;taint;manual",
]
}
}
@@ -253,21 +253,21 @@ module JsonNET {
override predicate row(string row) {
row =
[
- "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",
- "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",
- "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",
- "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",
- "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",
- "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",
- "Newtonsoft.Json.Linq;JObject;false;Parse;(System.String);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json.Linq;JObject;false;Parse;(System.String,Newtonsoft.Json.Linq.JsonLoadSettings);;Argument[0];ReturnValue;taint",
- "Newtonsoft.Json.Linq;JObject;false;get_Item;(System.Object);;Argument[Qualifier].Element;ReturnValue;value",
- "Newtonsoft.Json.Linq;JObject;false;get_Item;(System.Object);;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue;value",
- "Newtonsoft.Json.Linq;JObject;false;get_Item;(System.String);;Argument[Qualifier].Element;ReturnValue;value",
- "Newtonsoft.Json.Linq;JObject;false;set_Item;(System.Object,Newtonsoft.Json.Linq.JToken);;Argument[0];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value",
- "Newtonsoft.Json.Linq;JObject;false;set_Item;(System.Object,Newtonsoft.Json.Linq.JToken);;Argument[1];Argument[Qualifier].Element;value",
- "Newtonsoft.Json.Linq;JObject;false;set_Item;(System.Object,Newtonsoft.Json.Linq.JToken);;Argument[1];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value",
- "Newtonsoft.Json.Linq;JObject;false;set_Item;(System.String,Newtonsoft.Json.Linq.JToken);;Argument[1];Argument[Qualifier].Element;value",
+ "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;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",
+ "Newtonsoft.Json.Linq;JObject;false;get_Item;(System.Object);;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue;value;manual",
+ "Newtonsoft.Json.Linq;JObject;false;get_Item;(System.String);;Argument[Qualifier].Element;ReturnValue;value;manual",
+ "Newtonsoft.Json.Linq;JObject;false;set_Item;(System.Object,Newtonsoft.Json.Linq.JToken);;Argument[0];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual",
+ "Newtonsoft.Json.Linq;JObject;false;set_Item;(System.Object,Newtonsoft.Json.Linq.JToken);;Argument[1];Argument[Qualifier].Element;value;manual",
+ "Newtonsoft.Json.Linq;JObject;false;set_Item;(System.Object,Newtonsoft.Json.Linq.JToken);;Argument[1];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual",
+ "Newtonsoft.Json.Linq;JObject;false;set_Item;(System.String,Newtonsoft.Json.Linq.JToken);;Argument[1];Argument[Qualifier].Element;value;manual",
]
}
}
@@ -277,8 +277,8 @@ module JsonNET {
override predicate row(string row) {
row =
[
- "Newtonsoft.Json.Linq;JArray;false;get_Item;(System.Object);;Argument[Qualifier].Element;ReturnValue;value",
- "Newtonsoft.Json.Linq;JArray;false;set_Item;(System.Object,Newtonsoft.Json.Linq.JToken);;Argument[1];Argument[Qualifier].Element;value",
+ "Newtonsoft.Json.Linq;JArray;false;get_Item;(System.Object);;Argument[Qualifier].Element;ReturnValue;value;manual",
+ "Newtonsoft.Json.Linq;JArray;false;set_Item;(System.Object,Newtonsoft.Json.Linq.JToken);;Argument[1];Argument[Qualifier].Element;value;manual",
]
}
}
@@ -288,8 +288,8 @@ module JsonNET {
override predicate row(string row) {
row =
[
- "Newtonsoft.Json.Linq;JConstructor;false;get_Item;(System.Object);;Argument[Qualifier].Element;ReturnValue;value",
- "Newtonsoft.Json.Linq;JConstructor;false;set_Item;(System.Object,Newtonsoft.Json.Linq.JToken);;Argument[1];Argument[Qualifier].Element;value",
+ "Newtonsoft.Json.Linq;JConstructor;false;get_Item;(System.Object);;Argument[Qualifier].Element;ReturnValue;value;manual",
+ "Newtonsoft.Json.Linq;JConstructor;false;set_Item;(System.Object,Newtonsoft.Json.Linq.JToken);;Argument[1];Argument[Qualifier].Element;value;manual",
]
}
}
@@ -298,7 +298,7 @@ module JsonNET {
private class NewtonsoftJsonLinqJContainerFlowModelCsv extends SummaryModelCsv {
override predicate row(string row) {
row =
- "Newtonsoft.Json.Linq;JContainer;true;Add;(System.Object);;Argument[0];Argument[Qualifier].Element;value"
+ "Newtonsoft.Json.Linq;JContainer;true;Add;(System.Object);;Argument[0];Argument[Qualifier].Element;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 bf4a72b4174..1145933352f 100644
--- a/csharp/ql/lib/semmle/code/csharp/frameworks/ServiceStack.qll
+++ b/csharp/ql/lib/semmle/code/csharp/frameworks/ServiceStack.qll
@@ -47,89 +47,89 @@ private class ServiceStackRemoteSinkModelCsv extends SinkModelCsv {
row =
[
// IRestClient
- "ServiceStack;IRestClient;true;Send<>;(System.String,System.String,System.Object);;Argument[2];remote",
- "ServiceStack;IRestClient;true;Patch<>;(System.String,System.Object);;Argument[1];remote",
- "ServiceStack;IRestClient;true;Post<>;(System.String,System.Object);;Argument[1];remote",
- "ServiceStack;IRestClient;true;Put<>;(System.String,System.Object);;Argument[1];remote",
+ "ServiceStack;IRestClient;true;Send<>;(System.String,System.String,System.Object);;Argument[2];remote;manual",
+ "ServiceStack;IRestClient;true;Patch<>;(System.String,System.Object);;Argument[1];remote;manual",
+ "ServiceStack;IRestClient;true;Post<>;(System.String,System.Object);;Argument[1];remote;manual",
+ "ServiceStack;IRestClient;true;Put<>;(System.String,System.Object);;Argument[1];remote;manual",
// IRestClientSync
- "ServiceStack;IRestClientSync;true;CustomMethod;(System.String,ServiceStack.IReturnVoid);;Argument[1];remote",
- "ServiceStack;IRestClientSync;true;CustomMethod<>;(System.String,System.Object);;Argument[1];remote",
- "ServiceStack;IRestClientSync;true;CustomMethod<>;(System.String,ServiceStack.IReturn);;Argument[1];remote",
- "ServiceStack;IRestClientSync;true;Delete;(ServiceStack.IReturnVoid);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Delete<>;(System.Object);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Delete<>;(ServiceStack.IReturn);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Get;(ServiceStack.IReturnVoid);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Get<>;(System.Object);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Get<>;(ServiceStack.IReturn);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Patch;(ServiceStack.IReturnVoid);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Patch<>;(System.Object);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Patch<>;(ServiceStack.IReturn);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Post;(ServiceStack.IReturnVoid);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Post<>;(System.Object);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Post<>;(ServiceStack.IReturn);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Put;(ServiceStack.IReturnVoid);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Put<>;(System.Object);;Argument[0];remote",
- "ServiceStack;IRestClientSync;true;Put<>;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;IRestClientSync;true;CustomMethod;(System.String,ServiceStack.IReturnVoid);;Argument[1];remote;manual",
+ "ServiceStack;IRestClientSync;true;CustomMethod<>;(System.String,System.Object);;Argument[1];remote;manual",
+ "ServiceStack;IRestClientSync;true;CustomMethod<>;(System.String,ServiceStack.IReturn);;Argument[1];remote;manual",
+ "ServiceStack;IRestClientSync;true;Delete;(ServiceStack.IReturnVoid);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Delete<>;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Delete<>;(ServiceStack.IReturn);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Get;(ServiceStack.IReturnVoid);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Get<>;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Get<>;(ServiceStack.IReturn);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Patch;(ServiceStack.IReturnVoid);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Patch<>;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Patch<>;(ServiceStack.IReturn);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Post;(ServiceStack.IReturnVoid);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Post<>;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Post<>;(ServiceStack.IReturn);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Put;(ServiceStack.IReturnVoid);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Put<>;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientSync;true;Put<>;(ServiceStack.IReturn);;Argument[0];remote;manual",
// IRestGateway
- "ServiceStack;IRestGateway;true;Delete<>;(ServiceStack.IReturn);;Argument[0];remote",
- "ServiceStack;IRestGateway;true;Get<>;(ServiceStack.IReturn);;Argument[0];remote",
- "ServiceStack;IRestGateway;true;Post<>;(ServiceStack.IReturn);;Argument[0];remote",
- "ServiceStack;IRestGateway;true;Put<>;(ServiceStack.IReturn);;Argument[0];remote",
- "ServiceStack;IRestGateway;true;Send<>;(ServiceStack.IReturn);;Argument[0];remote",
+ "ServiceStack;IRestGateway;true;Delete<>;(ServiceStack.IReturn);;Argument[0];remote;manual",
+ "ServiceStack;IRestGateway;true;Get<>;(ServiceStack.IReturn);;Argument[0];remote;manual",
+ "ServiceStack;IRestGateway;true;Post<>;(ServiceStack.IReturn);;Argument[0];remote;manual",
+ "ServiceStack;IRestGateway;true;Put<>;(ServiceStack.IReturn);;Argument[0];remote;manual",
+ "ServiceStack;IRestGateway;true;Send<>;(ServiceStack.IReturn);;Argument[0];remote;manual",
// IOneWayClient
- "ServiceStack;IOneWayClient;true;SendAllOneWay;(System.Collections.Generic.IEnumerable);;Argument[1].Element;remote",
- "ServiceStack;IOneWayClient;true;SendOneWay;(System.String,System.Object);;Argument[1];remote",
- "ServiceStack;IOneWayClient;true;SendOneWay;(System.Object);;Argument[0];remote",
+ "ServiceStack;IOneWayClient;true;SendAllOneWay;(System.Collections.Generic.IEnumerable);;Argument[1].Element;remote;manual",
+ "ServiceStack;IOneWayClient;true;SendOneWay;(System.String,System.Object);;Argument[1];remote;manual",
+ "ServiceStack;IOneWayClient;true;SendOneWay;(System.Object);;Argument[0];remote;manual",
// IServiceGateway
- "ServiceStack;IServiceGateway;true;Publish;(System.Object);;Argument[0];remote",
- "ServiceStack;IServiceGateway;true;PublishAll;(System.Collections.Generic.IEnumerable);;Argument[0].Element;remote",
- "ServiceStack;IServiceGateway;true;Send<>;(System.Object);;Argument[0];remote",
- "ServiceStack;IServiceGateway;true;SendAll<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;remote",
+ "ServiceStack;IServiceGateway;true;Publish;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;IServiceGateway;true;PublishAll;(System.Collections.Generic.IEnumerable);;Argument[0].Element;remote;manual",
+ "ServiceStack;IServiceGateway;true;Send<>;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;IServiceGateway;true;SendAll<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;remote;manual",
// IRestClientAsync
- "ServiceStack;IRestClientAsync;true;CustomMethodAsync;(System.String,ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[1];remote",
- "ServiceStack;IRestClientAsync;true;CustomMethodAsync<>;(System.String,System.Object,System.Threading.CancellationToken);;Argument[1];remote",
- "ServiceStack;IRestClientAsync;true;CustomMethodAsync<>;(System.String,ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[1];remote",
- "ServiceStack;IRestClientAsync;true;DeleteAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;DeleteAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;DeleteAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;GetAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;GetAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;GetAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;PatchAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;PatchAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;PatchAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;PostAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;PostAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;PostAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;PutAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;PutAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestClientAsync;true;PutAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestClientAsync;true;CustomMethodAsync;(System.String,ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[1];remote;manual",
+ "ServiceStack;IRestClientAsync;true;CustomMethodAsync<>;(System.String,System.Object,System.Threading.CancellationToken);;Argument[1];remote;manual",
+ "ServiceStack;IRestClientAsync;true;CustomMethodAsync<>;(System.String,ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[1];remote;manual",
+ "ServiceStack;IRestClientAsync;true;DeleteAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;DeleteAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;DeleteAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;GetAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;GetAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;GetAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;PatchAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;PatchAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;PatchAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;PostAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;PostAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;PostAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;PutAsync;(ServiceStack.IReturnVoid,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;PutAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestClientAsync;true;PutAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote;manual",
// IRestGatewayAsync
- "ServiceStack;IRestGatewayAsync;true;DeleteAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestGatewayAsync;true;GetAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestGatewayAsync;true;PostAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestGatewayAsync;true;PutAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IRestGatewayAsync;true;SendAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote",
+ "ServiceStack;IRestGatewayAsync;true;DeleteAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestGatewayAsync;true;GetAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestGatewayAsync;true;PostAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestGatewayAsync;true;PutAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IRestGatewayAsync;true;SendAsync<>;(ServiceStack.IReturn,System.Threading.CancellationToken);;Argument[0];remote;manual",
// IServiceGatewayAsync
- "ServiceStack;IServiceGatewayAsync;true;PublishAsync;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IServiceGatewayAsync;true;PublishAllAsync;(System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[0].Element;remote",
- "ServiceStack;IServiceGatewayAsync;true;SendAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote",
- "ServiceStack;IServiceGatewayAsync;true;SendAllAsync<>;(System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[0].Element;remote",
+ "ServiceStack;IServiceGatewayAsync;true;PublishAsync;(System.Object,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IServiceGatewayAsync;true;PublishAllAsync;(System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[0].Element;remote;manual",
+ "ServiceStack;IServiceGatewayAsync;true;SendAsync<>;(System.Object,System.Threading.CancellationToken);;Argument[0];remote;manual",
+ "ServiceStack;IServiceGatewayAsync;true;SendAllAsync<>;(System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[0].Element;remote;manual",
// ServiceClientBase
- "ServiceStack;ServiceClientBase;true;Publish<>;(T);;Argument[0];remote",
- "ServiceStack;ServiceClientBase;true;Publish<>;(ServiceStack.Messaging.IMessage);;Argument[0];remote",
- "ServiceStack;ServiceClientBase;true;Delete;(System.Object);;Argument[0];remote",
- "ServiceStack;ServiceClientBase;true;Get;(System.Object);;Argument[0];remote",
- "ServiceStack;ServiceClientBase;true;Patch;(System.Object);;Argument[0];remote",
- "ServiceStack;ServiceClientBase;true;Post;(System.Object);;Argument[0];remote",
- "ServiceStack;ServiceClientBase;true;Put;(System.Object);;Argument[0];remote",
- "ServiceStack;ServiceClientBase;true;Head;(System.Object);;Argument[0];remote",
- "ServiceStack;ServiceClientBase;true;Head;(ServiceStack.IReturn);;Argument[0];remote",
- "ServiceStack;ServiceClientBase;true;CustomMethod;(System.String,System.String,System.Object);;Argument[2];remote",
- "ServiceStack;ServiceClientBase;true;CustomMethod<>;(System.String,System.String,System.Object);;Argument[2];remote",
- "ServiceStack;ServiceClientBase;true;CustomMethodAsync<>;(System.String,System.String,System.Object,System.Threading.CancellationToken);;Argument[2];remote",
- "ServiceStack;ServiceClientBase;true;DownloadBytes;(System.String,System.String,System.Object);;Argument[2];remote",
- "ServiceStack;ServiceClientBase;true;DownloadBytesAsync;(System.String,System.String,System.Object);;Argument[2];remote"
+ "ServiceStack;ServiceClientBase;true;Publish<>;(T);;Argument[0];remote;manual",
+ "ServiceStack;ServiceClientBase;true;Publish<>;(ServiceStack.Messaging.IMessage);;Argument[0];remote;manual",
+ "ServiceStack;ServiceClientBase;true;Delete;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;ServiceClientBase;true;Get;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;ServiceClientBase;true;Patch;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;ServiceClientBase;true;Post;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;ServiceClientBase;true;Put;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;ServiceClientBase;true;Head;(System.Object);;Argument[0];remote;manual",
+ "ServiceStack;ServiceClientBase;true;Head;(ServiceStack.IReturn);;Argument[0];remote;manual",
+ "ServiceStack;ServiceClientBase;true;CustomMethod;(System.String,System.String,System.Object);;Argument[2];remote;manual",
+ "ServiceStack;ServiceClientBase;true;CustomMethod<>;(System.String,System.String,System.Object);;Argument[2];remote;manual",
+ "ServiceStack;ServiceClientBase;true;CustomMethodAsync<>;(System.String,System.String,System.Object,System.Threading.CancellationToken);;Argument[2];remote;manual",
+ "ServiceStack;ServiceClientBase;true;DownloadBytes;(System.String,System.String,System.Object);;Argument[2];remote;manual",
+ "ServiceStack;ServiceClientBase;true;DownloadBytesAsync;(System.String,System.String,System.Object);;Argument[2];remote;manual"
]
}
}
@@ -139,104 +139,104 @@ private class ServiceStackSqlSinkModelCsv extends SinkModelCsv {
row =
[
// SqlExpression
- "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeAnd;(System.String,System.Object[]);;Argument[0];sql",
- "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeFrom;(System.String);;Argument[0];sql",
- "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeGroupBy;(System.String);;Argument[0];sql",
- "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeHaving;(System.String,System.Object[]);;Argument[0];sql",
- "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeOr;(System.String,System.Object[]);;Argument[0];sql",
- "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeOrderBy;(System.String);;Argument[0];sql",
- "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeSelect;(System.String,System.Boolean);;Argument[0];sql",
- "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeSelect;(System.String);;Argument[0];sql",
- "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeWhere;(System.String,System.Object[]);;Argument[0];sql",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeAnd;(System.String,System.Object[]);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeFrom;(System.String);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeGroupBy;(System.String);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeHaving;(System.String,System.Object[]);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeOr;(System.String,System.Object[]);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeOrderBy;(System.String);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeSelect;(System.String,System.Boolean);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeSelect;(System.String);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;SqlExpression<>;true;UnsafeWhere;(System.String,System.Object[]);;Argument[0];sql;manual",
// IUntypedSqlExpression
- "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeAnd;(System.String,System.Object[]);;Argument[0];sql",
- "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeFrom;(System.String);;Argument[0];sql",
- "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeOr;(System.String,System.Object[]);;Argument[0];sql",
- "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeSelect;(System.String);;Argument[0];sql",
- "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeWhere;(System.String,System.Object[]);;Argument[0];sql",
+ "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeAnd;(System.String,System.Object[]);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeFrom;(System.String);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeOr;(System.String,System.Object[]);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeSelect;(System.String);;Argument[0];sql;manual",
+ "ServiceStack.OrmLite;IUntypedSqlExpression;true;UnsafeWhere;(System.String,System.Object[]);;Argument[0];sql;manual",
// OrmLiteReadApi
- "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String,System.Action);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Exists<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Dictionary<,>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Lookup<,>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Lookup<,>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;KeyValuePairs;(System.Data.IDbConnection,System.String,System.System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Scalar<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Scalar<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.Type,System.String,System.Object);;Argument[2];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SelectLazy<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SelectNonDefaults<>;(System.Data.IDbConnection,System.String,T);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Single<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Single<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlColumn<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlColumn<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlColumn<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Action);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlScalar<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlScalar<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlScalar<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Column<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;Column<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnDistinct<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnDistinct<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnLazy<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnLazy<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String,System.Action);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ExecuteNonQuery;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Exists<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Dictionary<,>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Lookup<,>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Lookup<,>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;KeyValuePairs;(System.Data.IDbConnection,System.String,System.System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Scalar<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Scalar<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.Type,System.String,System.Object);;Argument[2];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Select<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SelectLazy<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SelectNonDefaults<>;(System.Data.IDbConnection,System.String,T);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Single<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Single<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlColumn<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlColumn<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlColumn<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlList<>;(System.Data.IDbConnection,System.String,System.Action);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlScalar<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlScalar<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;SqlScalar<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Column<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;Column<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnDistinct<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnDistinct<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnLazy<>;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApi;false;ColumnLazy<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
// OrmLiteReadExpressionsApi
- "ServiceStack.OrmLite;OrmLiteReadExpressionsApi;false;RowCount;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadExpressionsApi;false;RowCount;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadExpressionsApi;false;RowCount;(System.Data.IDbConnection,System.String,System.Object);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadExpressionsApi;false;RowCount;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable);;Argument[1];sql;manual",
// OrmLiteReadExpressionsApiAsync
- "ServiceStack.OrmLite;OrmLiteReadExpressionsApiAsync;false;RowCountAsync;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadExpressionsApiAsync;false;RowCountAsync;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
// OrmLiteReadApiAsync
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnDistinctAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnDistinctAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;DictionaryAsync<,>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExecuteNonQueryAsync;(System.Data.IDbConnection,System.String,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExecuteNonQueryAsync;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExecuteNonQueryAsync;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExistsAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;KeyValuePairsAsync<,>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;KeyValuePairsAsync<,>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;LookupAsync<,>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;LookupAsync<,>;(System.Data.IDbCommand,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;LookupAsync<,>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ScalarAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ScalarAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Threading.CancellationToken);;Argument[2];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectNonDefaultsAsync<>;(System.Data.IDbConnection,System.String,T,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SingleAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SingleAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlColumnAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlColumnAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlColumnAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Action,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlScalarAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlScalarAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql",
- "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlScalarAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnDistinctAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ColumnDistinctAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;DictionaryAsync<,>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExecuteNonQueryAsync;(System.Data.IDbConnection,System.String,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExecuteNonQueryAsync;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExecuteNonQueryAsync;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ExistsAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;KeyValuePairsAsync<,>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;KeyValuePairsAsync<,>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;LookupAsync<,>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;LookupAsync<,>;(System.Data.IDbCommand,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;LookupAsync<,>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ScalarAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;ScalarAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.Type,System.String,System.Object,System.Threading.CancellationToken);;Argument[2];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SelectNonDefaultsAsync<>;(System.Data.IDbConnection,System.String,T,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SingleAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SingleAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlColumnAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlColumnAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlColumnAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.Dictionary,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Collections.Generic.IEnumerable,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Object,System.Threading.CancellationToken);;Argument[1];sql;manual",
+ "ServiceStack.OrmLite;OrmLiteReadApiAsync;false;SqlListAsync<>;(System.Data.IDbConnection,System.String,System.Action