diff --git a/.github/actions/fetch-codeql/action.yml b/.github/actions/fetch-codeql/action.yml index 13b91525237..02098892663 100644 --- a/.github/actions/fetch-codeql/action.yml +++ b/.github/actions/fetch-codeql/action.yml @@ -3,22 +3,12 @@ description: Fetches the latest version of CodeQL runs: using: composite steps: - - name: Select platform - Linux - if: runner.os == 'Linux' - shell: bash - run: echo "GA_CODEQL_CLI_PLATFORM=linux64" >> $GITHUB_ENV - - - name: Select platform - MacOS - if: runner.os == 'MacOS' - shell: bash - run: echo "GA_CODEQL_CLI_PLATFORM=osx64" >> $GITHUB_ENV - - name: Fetch CodeQL shell: bash run: | - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-$GA_CODEQL_CLI_PLATFORM.zip "$LATEST" - unzip -q -d "${RUNNER_TEMP}" codeql-$GA_CODEQL_CLI_PLATFORM.zip - echo "${RUNNER_TEMP}/codeql" >> "${GITHUB_PATH}" + gh extension install github/gh-codeql + gh codeql set-channel nightly + gh codeql version + gh codeql version --format=json | jq -r .unpackedLocation >> "${GITHUB_PATH}" env: GITHUB_TOKEN: ${{ github.token }} diff --git a/.github/workflows/check-change-note.yml b/.github/workflows/check-change-note.yml index 672202444bb..b60a590ab09 100644 --- a/.github/workflows/check-change-note.yml +++ b/.github/workflows/check-change-note.yml @@ -10,6 +10,7 @@ on: - "*/ql/lib/**/*.qll" - "!**/experimental/**" - "!ql/**" + - "!swift/**" - ".github/workflows/check-change-note.yml" jobs: diff --git a/.github/workflows/check-qldoc.yml b/.github/workflows/check-qldoc.yml index 77f524b73e7..cc7523162aa 100644 --- a/.github/workflows/check-qldoc.yml +++ b/.github/workflows/check-qldoc.yml @@ -5,6 +5,7 @@ on: paths: - "*/ql/lib/**" - .github/workflows/check-qldoc.yml + - .github/actions/fetch-codeql/action.yml branches: - main - "rc/*" @@ -14,18 +15,13 @@ jobs: runs-on: ubuntu-latest steps: - - name: Install CodeQL - run: | - gh extension install github/gh-codeql - gh codeql set-channel nightly - gh codeql version - env: - GITHUB_TOKEN: ${{ github.token }} - - uses: actions/checkout@v3 with: fetch-depth: 2 + - name: Install CodeQL + uses: ./.github/actions/fetch-codeql + - name: Check QLdoc coverage shell: bash run: | @@ -34,7 +30,7 @@ jobs: changed_lib_packs="$(git diff --name-only --diff-filter=ACMRT HEAD^ HEAD | { grep -Po '^(?!swift)[a-z]*/ql/lib' || true; } | sort -u)" for pack_dir in ${changed_lib_packs}; do lang="${pack_dir%/ql/lib}" - gh codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-current.txt" --dir="${pack_dir}" + codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-current.txt" --dir="${pack_dir}" done git checkout HEAD^ for pack_dir in ${changed_lib_packs}; do @@ -42,7 +38,7 @@ jobs: # In this case the right thing to do is to skip the check. [[ ! -d "${pack_dir}" ]] && continue lang="${pack_dir%/ql/lib}" - gh codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-baseline.txt" --dir="${pack_dir}" + codeql generate library-doc-coverage --output="${RUNNER_TEMP}/${lang}-baseline.txt" --dir="${pack_dir}" awk -F, '{gsub(/"/,""); if ($4==0 && $6=="public") print "\""$3"\"" }' "${RUNNER_TEMP}/${lang}-current.txt" | sort -u > "${RUNNER_TEMP}/current-undocumented.txt" awk -F, '{gsub(/"/,""); if ($4==0 && $6=="public") print "\""$3"\"" }' "${RUNNER_TEMP}/${lang}-baseline.txt" | sort -u > "${RUNNER_TEMP}/baseline-undocumented.txt" UNDOCUMENTED="$(grep -f <(comm -13 "${RUNNER_TEMP}/baseline-undocumented.txt" "${RUNNER_TEMP}/current-undocumented.txt") "${RUNNER_TEMP}/${lang}-current.txt" || true)" diff --git a/.github/workflows/csv-coverage-metrics.yml b/.github/workflows/csv-coverage-metrics.yml index 7778221dc2f..7555533ab98 100644 --- a/.github/workflows/csv-coverage-metrics.yml +++ b/.github/workflows/csv-coverage-metrics.yml @@ -12,6 +12,7 @@ on: - main paths: - ".github/workflows/csv-coverage-metrics.yml" + - ".github/actions/fetch-codeql/action.yml" jobs: publish-java: diff --git a/.github/workflows/csv-coverage-pr-artifacts.yml b/.github/workflows/csv-coverage-pr-artifacts.yml index 379b3c5aad8..19ad488a3ab 100644 --- a/.github/workflows/csv-coverage-pr-artifacts.yml +++ b/.github/workflows/csv-coverage-pr-artifacts.yml @@ -3,18 +3,20 @@ name: Check framework coverage changes on: pull_request: paths: - - '.github/workflows/csv-coverage-pr-comment.yml' - - '*/ql/src/**/*.ql' - - '*/ql/src/**/*.qll' - - '*/ql/lib/**/*.ql' - - '*/ql/lib/**/*.qll' - - 'misc/scripts/library-coverage/*.py' + - ".github/workflows/csv-coverage-pr-comment.yml" + - ".github/workflows/csv-coverage-pr-artifacts.yml" + - ".github/actions/fetch-codeql/action.yml" + - "*/ql/src/**/*.ql" + - "*/ql/src/**/*.qll" + - "*/ql/lib/**/*.ql" + - "*/ql/lib/**/*.qll" + - "misc/scripts/library-coverage/*.py" # input data files - - '*/documentation/library-coverage/cwe-sink.csv' - - '*/documentation/library-coverage/frameworks.csv' + - "*/documentation/library-coverage/cwe-sink.csv" + - "*/documentation/library-coverage/frameworks.csv" branches: - main - - 'rc/*' + - "rc/*" jobs: generate: @@ -23,77 +25,72 @@ jobs: runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJSON(github.event) }} - run: echo "$GITHUB_CONTEXT" - - name: Clone self (github/codeql) - MERGE - uses: actions/checkout@v3 - with: - path: merge - - name: Clone self (github/codeql) - BASE - uses: actions/checkout@v3 - with: - fetch-depth: 2 - path: base - - run: | - git checkout HEAD^1 - git log -1 --format='%H' - working-directory: base - - name: Set up Python 3.8 - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Download CodeQL CLI - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip" - - name: Unzip CodeQL CLI - run: unzip -d codeql-cli codeql-linux64.zip - - name: Generate CSV files on merge commit of the PR - run: | - echo "Running generator on merge" - PATH="$PATH:codeql-cli/codeql" python merge/misc/scripts/library-coverage/generate-report.py ci merge merge - mkdir out_merge - cp framework-coverage-*.csv out_merge/ - cp framework-coverage-*.rst out_merge/ - - name: Generate CSV files on base commit of the PR - run: | - echo "Running generator on base" - PATH="$PATH:codeql-cli/codeql" python base/misc/scripts/library-coverage/generate-report.py ci base base - mkdir out_base - cp framework-coverage-*.csv out_base/ - cp framework-coverage-*.rst out_base/ - - name: Generate diff of coverage reports - run: | - python base/misc/scripts/library-coverage/compare-folders.py out_base out_merge comparison.md - - name: Upload CSV package list - uses: actions/upload-artifact@v3 - with: - name: csv-framework-coverage-merge - path: | - out_merge/framework-coverage-*.csv - out_merge/framework-coverage-*.rst - - name: Upload CSV package list - uses: actions/upload-artifact@v3 - with: - name: csv-framework-coverage-base - path: | - out_base/framework-coverage-*.csv - out_base/framework-coverage-*.rst - - name: Upload comparison results - uses: actions/upload-artifact@v3 - with: - name: comparison - path: | - comparison.md - - name: Save PR number - run: | - mkdir -p pr - echo ${{ github.event.pull_request.number }} > pr/NR - - name: Upload PR number - uses: actions/upload-artifact@v3 - with: - name: pr - path: pr/ + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJSON(github.event) }} + run: echo "$GITHUB_CONTEXT" + - name: Clone self (github/codeql) - MERGE + uses: actions/checkout@v3 + with: + path: merge + - name: Clone self (github/codeql) - BASE + uses: actions/checkout@v3 + with: + fetch-depth: 2 + path: base + - run: | + git checkout HEAD^1 + git log -1 --format='%H' + working-directory: base + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Download CodeQL CLI + uses: ./merge/.github/actions/fetch-codeql + - name: Generate CSV files on merge commit of the PR + run: | + echo "Running generator on merge" + python merge/misc/scripts/library-coverage/generate-report.py ci merge merge + mkdir out_merge + cp framework-coverage-*.csv out_merge/ + cp framework-coverage-*.rst out_merge/ + - name: Generate CSV files on base commit of the PR + run: | + echo "Running generator on base" + python base/misc/scripts/library-coverage/generate-report.py ci base base + mkdir out_base + cp framework-coverage-*.csv out_base/ + cp framework-coverage-*.rst out_base/ + - name: Generate diff of coverage reports + run: | + python base/misc/scripts/library-coverage/compare-folders.py out_base out_merge comparison.md + - name: Upload CSV package list + uses: actions/upload-artifact@v3 + with: + name: csv-framework-coverage-merge + path: | + out_merge/framework-coverage-*.csv + out_merge/framework-coverage-*.rst + - name: Upload CSV package list + uses: actions/upload-artifact@v3 + with: + name: csv-framework-coverage-base + path: | + out_base/framework-coverage-*.csv + out_base/framework-coverage-*.rst + - name: Upload comparison results + uses: actions/upload-artifact@v3 + with: + name: comparison + path: | + comparison.md + - name: Save PR number + run: | + mkdir -p pr + echo ${{ github.event.pull_request.number }} > pr/NR + - name: Upload PR number + uses: actions/upload-artifact@v3 + with: + name: pr + path: pr/ diff --git a/.github/workflows/csv-coverage-timeseries.yml b/.github/workflows/csv-coverage-timeseries.yml index 95b084ea215..42fd4711dac 100644 --- a/.github/workflows/csv-coverage-timeseries.yml +++ b/.github/workflows/csv-coverage-timeseries.yml @@ -5,38 +5,29 @@ on: jobs: build: - runs-on: ubuntu-latest steps: - - name: Clone self (github/codeql) - uses: actions/checkout@v3 - with: - path: script - - name: Clone self (github/codeql) for analysis - uses: actions/checkout@v3 - with: - path: codeqlModels - fetch-depth: 0 - - name: Set up Python 3.8 - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Download CodeQL CLI - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip" - - name: Unzip CodeQL CLI - run: unzip -d codeql-cli codeql-linux64.zip - - name: Build modeled package list - run: | - CLI=$(realpath "codeql-cli/codeql") - echo $CLI - PATH="$PATH:$CLI" python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels - - name: Upload timeseries CSV - uses: actions/upload-artifact@v3 - with: - name: framework-coverage-timeseries - path: framework-coverage-timeseries-*.csv - + - name: Clone self (github/codeql) + uses: actions/checkout@v3 + with: + path: script + - name: Clone self (github/codeql) for analysis + uses: actions/checkout@v3 + with: + path: codeqlModels + fetch-depth: 0 + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Download CodeQL CLI + uses: ./script/.github/actions/fetch-codeql + - name: Build modeled package list + run: | + python script/misc/scripts/library-coverage/generate-timeseries.py codeqlModels + - name: Upload timeseries CSV + uses: actions/upload-artifact@v3 + with: + name: framework-coverage-timeseries + path: framework-coverage-timeseries-*.csv diff --git a/.github/workflows/csv-coverage-update.yml b/.github/workflows/csv-coverage-update.yml index c57056b6de1..6474044c483 100644 --- a/.github/workflows/csv-coverage-update.yml +++ b/.github/workflows/csv-coverage-update.yml @@ -12,33 +12,27 @@ jobs: runs-on: ubuntu-latest steps: - - name: Dump GitHub context - env: - GITHUB_CONTEXT: ${{ toJSON(github.event) }} - run: echo "$GITHUB_CONTEXT" - - name: Clone self (github/codeql) - uses: actions/checkout@v3 - with: - path: ql - fetch-depth: 0 - - name: Set up Python 3.8 - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Download CodeQL CLI - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip" - - name: Unzip CodeQL CLI - run: unzip -d codeql-cli codeql-linux64.zip + - name: Dump GitHub context + env: + GITHUB_CONTEXT: ${{ toJSON(github.event) }} + run: echo "$GITHUB_CONTEXT" + - name: Clone self (github/codeql) + uses: actions/checkout@v3 + with: + path: ql + fetch-depth: 0 + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Download CodeQL CLI + uses: ./ql/.github/actions/fetch-codeql + - name: Generate coverage files + run: | + python ql/misc/scripts/library-coverage/generate-report.py ci ql ql - - name: Generate coverage files - run: | - PATH="$PATH:codeql-cli/codeql" python ql/misc/scripts/library-coverage/generate-report.py ci ql ql - - - name: Create pull request with changes - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - python ql/misc/scripts/library-coverage/create-pr.py ql "$GITHUB_REPOSITORY" + - name: Create pull request with changes + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + python ql/misc/scripts/library-coverage/create-pr.py ql "$GITHUB_REPOSITORY" diff --git a/.github/workflows/csv-coverage.yml b/.github/workflows/csv-coverage.yml index 9a308d50265..e330490b69b 100644 --- a/.github/workflows/csv-coverage.yml +++ b/.github/workflows/csv-coverage.yml @@ -4,46 +4,39 @@ on: workflow_dispatch: inputs: qlModelShaOverride: - description: 'github/codeql repo SHA used for looking up the CSV models' + description: "github/codeql repo SHA used for looking up the CSV models" required: false jobs: build: - runs-on: ubuntu-latest steps: - - name: Clone self (github/codeql) - uses: actions/checkout@v3 - with: - path: script - - name: Clone self (github/codeql) for analysis - uses: actions/checkout@v3 - with: - path: codeqlModels - ref: ${{ github.event.inputs.qlModelShaOverride || github.ref }} - - name: Set up Python 3.8 - uses: actions/setup-python@v4 - with: - python-version: 3.8 - - name: Download CodeQL CLI - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - gh release download --repo "github/codeql-cli-binaries" --pattern "codeql-linux64.zip" - - name: Unzip CodeQL CLI - run: unzip -d codeql-cli codeql-linux64.zip - - name: Build modeled package list - run: | - PATH="$PATH:codeql-cli/codeql" python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script - - name: Upload CSV package list - uses: actions/upload-artifact@v3 - with: - name: framework-coverage-csv - path: framework-coverage-*.csv - - name: Upload RST package list - uses: actions/upload-artifact@v3 - with: - name: framework-coverage-rst - path: framework-coverage-*.rst - + - name: Clone self (github/codeql) + uses: actions/checkout@v3 + with: + path: script + - name: Clone self (github/codeql) for analysis + uses: actions/checkout@v3 + with: + path: codeqlModels + ref: ${{ github.event.inputs.qlModelShaOverride || github.ref }} + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Download CodeQL CLI + uses: ./script/.github/actions/fetch-codeql + - name: Build modeled package list + run: | + python script/misc/scripts/library-coverage/generate-report.py ci codeqlModels script + - name: Upload CSV package list + uses: actions/upload-artifact@v3 + with: + name: framework-coverage-csv + path: framework-coverage-*.csv + - name: Upload RST package list + uses: actions/upload-artifact@v3 + with: + name: framework-coverage-rst + path: framework-coverage-*.rst diff --git a/.github/workflows/go-tests.yml b/.github/workflows/go-tests.yml index ca126d1a3ee..26055bd71da 100644 --- a/.github/workflows/go-tests.yml +++ b/.github/workflows/go-tests.yml @@ -4,159 +4,111 @@ on: paths: - "go/**" - .github/workflows/go-tests.yml + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml jobs: - test-linux: name: Test Linux (Ubuntu) runs-on: ubuntu-latest steps: + - name: Set up Go 1.18.1 + uses: actions/setup-go@v3 + with: + go-version: 1.18.1 + id: go - - name: Set up Go 1.18.1 - uses: actions/setup-go@v3 - with: - go-version: 1.18.1 - id: go + - name: Check out code + uses: actions/checkout@v2 - - name: Set up CodeQL CLI - run: | - echo "Removing old CodeQL Directory..." - rm -rf $HOME/codeql - echo "Done" - cd $HOME - echo "Downloading CodeQL CLI..." - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST" - echo "Done" - echo "Unpacking CodeQL CLI..." - unzip -q codeql-linux64.zip - rm -f codeql-linux64.zip - echo "Done" - env: - GITHUB_TOKEN: ${{ github.token }} + - name: Set up CodeQL CLI + uses: ./.github/actions/fetch-codeql - - name: Check out code - uses: actions/checkout@v2 + - name: Enable problem matchers in repository + shell: bash + run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' - - name: Enable problem matchers in repository - shell: bash - run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' + - name: Build + run: | + cd go + make - - name: Build - run: | - cd go - env PATH=$PATH:$HOME/codeql make + - name: Check that all QL and Go code is autoformatted + run: | + cd go + make check-formatting - - name: Check that all QL and Go code is autoformatted - run: | - cd go - env PATH=$PATH:$HOME/codeql make check-formatting + - name: Compile qhelp files to markdown + run: | + cd go + env QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown - - name: Compile qhelp files to markdown - run: | - cd go - env PATH=$PATH:$HOME/codeql QHELP_OUT_DIR=qhelp-out make qhelp-to-markdown + - name: Upload qhelp markdown + uses: actions/upload-artifact@v2 + with: + name: qhelp-markdown + path: go/qhelp-out/**/*.md - - name: Upload qhelp markdown - uses: actions/upload-artifact@v2 - with: - name: qhelp-markdown - path: go/qhelp-out/**/*.md - - - name: Test - run: | - cd go - env PATH=$PATH:$HOME/codeql make test + - name: Test + run: | + cd go + make test test-mac: name: Test MacOS - runs-on: macOS-latest + runs-on: macos-latest steps: - - name: Set up Go 1.18.1 - uses: actions/setup-go@v3 - with: - go-version: 1.18.1 - id: go + - name: Set up Go 1.18.1 + uses: actions/setup-go@v3 + with: + go-version: 1.18.1 + id: go - - name: Set up CodeQL CLI - run: | - echo "Removing old CodeQL Directory..." - rm -rf $HOME/codeql - echo "Done" - cd $HOME - echo "Downloading CodeQL CLI..." - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-osx64.zip "$LATEST" - echo "Done" - echo "Unpacking CodeQL CLI..." - unzip -q codeql-osx64.zip - rm -f codeql-osx64.zip - echo "Done" - env: - GITHUB_TOKEN: ${{ github.token }} + - name: Check out code + uses: actions/checkout@v2 - - name: Check out code - uses: actions/checkout@v2 + - name: Set up CodeQL CLI + uses: ./.github/actions/fetch-codeql - - name: Enable problem matchers in repository - shell: bash - run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' + - name: Enable problem matchers in repository + shell: bash + run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' - - name: Build - run: | - cd go - env PATH=$PATH:$HOME/codeql make + - name: Build + run: | + cd go + make - - name: Test - run: | - cd go - env PATH=$PATH:$HOME/codeql make test + - name: Test + run: | + cd go + make test test-win: name: Test Windows runs-on: windows-2019 steps: - - name: Set up Go 1.18.1 - uses: actions/setup-go@v3 - with: - go-version: 1.18.1 - id: go + - name: Set up Go 1.18.1 + uses: actions/setup-go@v3 + with: + go-version: 1.18.1 + id: go - - name: Set up CodeQL CLI - run: | - echo "Removing old CodeQL Directory..." - rm -rf $HOME/codeql - echo "Done" - cd "$HOME" - echo "Downloading CodeQL CLI..." - LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | grep -v beta | tail -1) - gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-win64.zip "$LATEST" - echo "Done" - echo "Unpacking CodeQL CLI..." - unzip -q -o codeql-win64.zip - unzip -q -o codeql-win64.zip codeql/codeql.exe - rm -f codeql-win64.zip - echo "Done" - env: - GITHUB_TOKEN: ${{ github.token }} - shell: - bash + - name: Check out code + uses: actions/checkout@v2 - - name: Check out code - uses: actions/checkout@v2 + - name: Set up CodeQL CLI + uses: ./.github/actions/fetch-codeql - - name: Enable problem matchers in repository - shell: bash - run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' + - name: Enable problem matchers in repository + shell: bash + run: 'find .github/problem-matchers -name \*.json -exec echo "::add-matcher::{}" \;' - - name: Build - run: | - $Env:Path += ";$HOME\codeql" - cd go - make + - name: Build + run: | + cd go + make - - name: Test - run: | - $Env:Path += ";$HOME\codeql" - cd go - make test + - name: Test + run: | + cd go + make test diff --git a/.github/workflows/js-ml-tests.yml b/.github/workflows/js-ml-tests.yml index 65db215d8c3..c932432530b 100644 --- a/.github/workflows/js-ml-tests.yml +++ b/.github/workflows/js-ml-tests.yml @@ -5,6 +5,7 @@ on: paths: - "javascript/ql/experimental/adaptivethreatmodeling/**" - .github/workflows/js-ml-tests.yml + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml branches: - main @@ -13,6 +14,7 @@ on: paths: - "javascript/ql/experimental/adaptivethreatmodeling/**" - .github/workflows/js-ml-tests.yml + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml workflow_dispatch: diff --git a/.github/workflows/mad_regenerate-models.yml b/.github/workflows/mad_regenerate-models.yml index d1d7e6e3791..0abc8936911 100644 --- a/.github/workflows/mad_regenerate-models.yml +++ b/.github/workflows/mad_regenerate-models.yml @@ -9,6 +9,7 @@ on: - main paths: - ".github/workflows/mad_regenerate-models.yml" + - ".github/actions/fetch-codeql/action.yml" jobs: regenerate-models: diff --git a/.github/workflows/ql-for-ql-build.yml b/.github/workflows/ql-for-ql-build.yml index 6b4f6a0abee..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 46a27709717..5700045430d 100644 --- a/.github/workflows/swift-codegen.yml +++ b/.github/workflows/swift-codegen.yml @@ -5,6 +5,7 @@ on: paths: - "swift/**" - .github/workflows/swift-codegen.yml + - .github/actions/fetch-codeql/action.yml branches: - main diff --git a/.github/workflows/swift-integration-tests.yml b/.github/workflows/swift-integration-tests.yml new file mode 100644 index 00000000000..4d4248b64e3 --- /dev/null +++ b/.github/workflows/swift-integration-tests.yml @@ -0,0 +1,35 @@ +name: "Swift: Run Integration Tests" + +on: + pull_request: + paths: + - "swift/**" + - .github/workflows/swift-integration-tests.yml + - .github/actions/fetch-codeql/action.yml + - codeql-workspace.yml + branches: + - main +defaults: + run: + working-directory: swift + +jobs: + integration-tests: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-20.04 +# - macos-latest TODO + steps: + - uses: actions/checkout@v3 + - uses: ./.github/actions/fetch-codeql + - uses: bazelbuild/setup-bazelisk@v2 + - uses: actions/setup-python@v3 + - name: Build Swift extractor + run: | + bazel run //swift:create-extractor-pack + - name: Run integration tests + run: | + python integration-tests/runner.py diff --git a/.github/workflows/swift-qltest.yml b/.github/workflows/swift-qltest.yml index 915e1f331a5..3cbcf629c98 100644 --- a/.github/workflows/swift-qltest.yml +++ b/.github/workflows/swift-qltest.yml @@ -5,6 +5,7 @@ on: paths: - "swift/**" - .github/workflows/swift-qltest.yml + - .github/actions/fetch-codeql/action.yml - codeql-workspace.yml branches: - main diff --git a/.github/workflows/validate-change-notes.yml b/.github/workflows/validate-change-notes.yml index 798913746be..44e0dc6df29 100644 --- a/.github/workflows/validate-change-notes.yml +++ b/.github/workflows/validate-change-notes.yml @@ -5,6 +5,7 @@ on: paths: - "*/ql/*/change-notes/**/*" - ".github/workflows/validate-change-notes.yml" + - ".github/actions/fetch-codeql/action.yml" branches: - main - "rc/*" @@ -12,6 +13,7 @@ on: paths: - "*/ql/*/change-notes/**/*" - ".github/workflows/validate-change-notes.yml" + - ".github/actions/fetch-codeql/action.yml" jobs: check-change-note: diff --git a/CODEOWNERS b/CODEOWNERS index da71d1ec5d8..1754d58af63 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -42,3 +42,4 @@ WORKSPACE.bazel @github/codeql-ci-reviewers /.github/workflows/js-ml-tests.yml @github/codeql-ml-powered-queries-reviewers /.github/workflows/ql-for-ql-* @github/codeql-ql-for-ql-reviewers /.github/workflows/ruby-* @github/codeql-ruby +/.github/workflows/swift-* @github/codeql-c diff --git a/config/identical-files.json b/config/identical-files.json index 81e07fe48cf..87b3e876e10 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -393,7 +393,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", 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 c1cefbed8f9..9b4761ec2ce 100644 --- a/cpp/ql/lib/CHANGELOG.md +++ b/cpp/ql/lib/CHANGELOG.md @@ -1,3 +1,15 @@ +## 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 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/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 95f6e3a0ba6..18c64250f42 100644 --- a/cpp/ql/lib/codeql-pack.release.yml +++ b/cpp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.2 diff --git a/cpp/ql/lib/qlpack.yml b/cpp/ql/lib/qlpack.yml index a20077271d7..06e68dba48c 100644 --- a/cpp/ql/lib/qlpack.yml +++ b/cpp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-all -version: 0.3.1-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/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 a076f7a2e45..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..340bfe280b7 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,7 +3272,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll index da8e7564b3e..79d9f85464e 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowUtil.qll @@ -699,7 +699,7 @@ private predicate exprToExprStep_nocfg(Expr fromExpr, Expr toExpr) { call.getTarget() = f and // AST dataflow treats a reference as if it were the referred-to object, while the dataflow // models treat references as pointers. If the return type of the call is a reference, then - // look for data flow the the referred-to object, rather than the reference itself. + // look for data flow to the referred-to object, rather than the reference itself. if call.getType().getUnspecifiedType() instanceof ReferenceType then outModel.isReturnValueDeref() else outModel.isReturnValue() diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll index 309d98cd694..979c9c03940 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/BuiltInOperations.qll @@ -1,5 +1,5 @@ /** - * Provides classes for modeling built-in operations. Built-in operations are + * Provides classes for modeling built-in operations. Built-in operations are * typically compiler specific and are used by libraries and generated code. */ @@ -120,8 +120,8 @@ class BuiltInNoOp extends BuiltInOperation, @noopexpr { /** * A C/C++ `__builtin_offsetof` built-in operation (used by some implementations - * of `offsetof`). The operation retains its semantics even in the presence - * of an overloaded `operator &`). This is a GNU/Clang extension. + * of `offsetof`). The operation retains its semantics even in the presence + * of an overloaded `operator &`). This is a gcc/clang extension. * ``` * struct S { * int a, b; @@ -137,8 +137,8 @@ class BuiltInOperationBuiltInOffsetOf extends BuiltInOperation, @offsetofexpr { /** * A C/C++ `__INTADDR__` built-in operation (used by some implementations - * of `offsetof`). The operation retains its semantics even in the presence - * of an overloaded `operator &`). This is an EDG extension. + * of `offsetof`). The operation retains its semantics even in the presence + * of an overloaded `operator &`). This is an EDG extension. * ``` * struct S { * int a, b; @@ -173,7 +173,7 @@ class BuiltInOperationHasAssign extends BuiltInOperation, @hasassignexpr { * * Returns `true` if the type has a copy constructor. * ``` - * std::integral_constant< bool, __has_copy(_Tp)> hc; + * std::integral_constant hc; * ``` */ class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr { @@ -189,7 +189,7 @@ class BuiltInOperationHasCopy extends BuiltInOperation, @hascopyexpr { * Returns `true` if a copy assignment operator has an empty exception * specification. * ``` - * std::integral_constant< bool, __has_nothrow_assign(_Tp)> hnta; + * std::integral_constant hnta; * ``` */ class BuiltInOperationHasNoThrowAssign extends BuiltInOperation, @hasnothrowassign { @@ -220,7 +220,7 @@ class BuiltInOperationHasNoThrowConstructor extends BuiltInOperation, @hasnothro * * Returns `true` if the copy constructor has an empty exception specification. * ``` - * std::integral_constant< bool, __has_nothrow_copy(MyType) >; + * std::integral_constant; * ``` */ class BuiltInOperationHasNoThrowCopy extends BuiltInOperation, @hasnothrowcopy { @@ -266,7 +266,7 @@ class BuiltInOperationHasTrivialConstructor extends BuiltInOperation, @hastrivia * * Returns true if the type has a trivial copy constructor. * ``` - * std::integral_constant< bool, __has_trivial_copy(MyType) > htc; + * std::integral_constant htc; * ``` */ class BuiltInOperationHasTrivialCopy extends BuiltInOperation, @hastrivialcopy { @@ -468,7 +468,7 @@ class BuiltInOperationIsUnion extends BuiltInOperation, @isunionexpr { * ``` * template * struct types_compatible - * : public integral_constant + * : public integral_constant * { }; * ``` */ @@ -479,8 +479,7 @@ class BuiltInOperationBuiltInTypesCompatibleP extends BuiltInOperation, @typesco /** * A clang `__builtin_shufflevector` expression. * - * It outputs a permutation of elements from one or two input vectors. - * Please see + * It outputs a permutation of elements from one or two input vectors. See * https://releases.llvm.org/3.7.0/tools/clang/docs/LanguageExtensions.html#langext-builtin-shufflevector * for more information. * ``` @@ -494,11 +493,29 @@ class BuiltInOperationBuiltInShuffleVector extends BuiltInOperation, @builtinshu override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInShuffleVector" } } +/** + * A gcc `__builtin_shuffle` expression. + * + * It outputs a permutation of elements from one or two input vectors. + * See https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html + * for more information. + * ``` + * // Concatenate every other element of 4-element vectors V1 and V2. + * M = {0, 2, 4, 6}; + * V3 = __builtin_shuffle(V1, V2, M); + * ``` + */ +class BuiltInOperationBuiltInShuffle extends BuiltInOperation, @builtinshuffle { + override string toString() { result = "__builtin_shuffle" } + + override string getAPrimaryQlClass() { result = "BuiltInOperationBuiltInShuffle" } +} + /** * A clang `__builtin_convertvector` expression. * * Allows for conversion of vectors of equal element count and compatible - * element types. Please see + * element types. See * https://releases.llvm.org/3.7.0/tools/clang/docs/LanguageExtensions.html#builtin-convertvector * for more information. * ``` @@ -547,7 +564,7 @@ class BuiltInOperationBuiltInAddressOf extends UnaryOperation, BuiltInOperation, * ``` * template * struct is_trivially_constructible - * : public integral_constant + * : public integral_constant * { }; * ``` */ @@ -612,13 +629,10 @@ class BuiltInOperationIsTriviallyDestructible extends BuiltInOperation, @istrivi * The `__is_trivially_assignable` built-in operation (used by some * implementations of the `` header). * - * Returns `true` if the assignment operator `C::operator =(const C& c)` is - * trivial. + * Returns `true` if the assignment operator `C::operator =(const D& d)` is + * trivial (i.e., it will not call any operation that is non-trivial). * ``` - * template - * struct is_trivially_assignable - * : public integral_constant - * { }; + * bool v = __is_trivially_assignable(MyType1, MyType2); * ``` */ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istriviallyassignableexpr { @@ -631,10 +645,10 @@ class BuiltInOperationIsTriviallyAssignable extends BuiltInOperation, @istrivial * The `__is_nothrow_assignable` built-in operation (used by some * implementations of the `` header). * - * Returns true if there exists a `C::operator =(const C& c) nothrow` + * Returns true if there exists a `C::operator =(const D& d) nothrow` * assignment operator (i.e, with an empty exception specification). * ``` - * bool v = __is_nothrow_assignable(MyType); + * bool v = __is_nothrow_assignable(MyType1, MyType2); * ``` */ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowassignableexpr { @@ -643,15 +657,30 @@ class BuiltInOperationIsNothrowAssignable extends BuiltInOperation, @isnothrowas override string getAPrimaryQlClass() { result = "BuiltInOperationIsNothrowAssignable" } } +/** + * The `__is_assignable` built-in operation (used by some implementations + * of the `` header). + * + * Returns true if there exists a `C::operator =(const D& d)` assignment + * operator. + * ``` + * bool v = __is_assignable(MyType1, MyType2); + * ``` + */ +class BuiltInOperationIsAssignable extends BuiltInOperation, @isassignable { + override string toString() { result = "__is_assignable" } + + override string getAPrimaryQlClass() { result = "BuiltInOperationIsAssignable" } +} + /** * The `__is_standard_layout` built-in operation (used by some implementations * of the `` header). * * Returns `true` if the type is a primitive type, or a `class`, `struct` or - * `union` WITHOUT (1) virtual functions or base classes, (2) reference member - * variable or (3) multiple occurrences of base `class` objects, among other - * restrictions. Please see - * https://en.cppreference.com/w/cpp/named_req/StandardLayoutType + * `union` without (1) virtual functions or base classes, (2) reference member + * variable, or (3) multiple occurrences of base `class` objects, among other + * restrictions. See https://en.cppreference.com/w/cpp/named_req/StandardLayoutType * for more information. * ``` * bool v = __is_standard_layout(MyType); @@ -668,7 +697,7 @@ class BuiltInOperationIsStandardLayout extends BuiltInOperation, @isstandardlayo * implementations of the `` header). * * Returns `true` if instances of this type can be copied by trivial - * means. The copying is done in a manner similar to the `memcpy` + * means. The copying is done in a manner similar to the `memcpy` * function. */ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istriviallycopyableexpr { @@ -682,13 +711,13 @@ class BuiltInOperationIsTriviallyCopyable extends BuiltInOperation, @istrivially * the `` header). * * Returns `true` if the type is a scalar type, a reference type or an array of - * literal types, among others. Please see + * literal types, among others. See * https://en.cppreference.com/w/cpp/named_req/LiteralType * for more information. * * ``` * template - * std::integral_constant< bool, __is_literal_type(_Tp)> ilt; + * std::integral_constant ilt; * ``` */ class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr { @@ -705,7 +734,7 @@ class BuiltInOperationIsLiteralType extends BuiltInOperation, @isliteraltypeexpr * compiler, with semantics of the `memcpy` operation. * ``` * template - * std::integral_constant< bool, __has_trivial_move_constructor(_Tp)> htmc; + * std::integral_constant htmc; * ``` */ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation, @@ -723,7 +752,7 @@ class BuiltInOperationHasTrivialMoveConstructor extends BuiltInOperation, * ``` * template * struct has_trivial_move_assign - * : public integral_constant + * : public integral_constant * { }; * ``` */ @@ -758,7 +787,7 @@ class BuiltInOperationHasNothrowMoveAssign extends BuiltInOperation, @hasnothrow * ``` * template * struct is_constructible - * : public integral_constant + * : public integral_constant * { }; * ``` */ @@ -785,7 +814,7 @@ class BuiltInOperationIsNothrowConstructible extends BuiltInOperation, @isnothro } /** - * The `__has_finalizer` built-in operation. This is a Microsoft extension. + * The `__has_finalizer` built-in operation. This is a Microsoft extension. * * Returns `true` if the type defines a _finalizer_ `C::!C(void)`, to be called * from either the regular destructor or the garbage collector. @@ -800,10 +829,10 @@ class BuiltInOperationHasFinalizer extends BuiltInOperation, @hasfinalizerexpr { } /** - * The `__is_delegate` built-in operation. This is a Microsoft extension. + * The `__is_delegate` built-in operation. This is a Microsoft extension. * * Returns `true` if the function has been declared as a `delegate`, used in - * message forwarding. Please see + * message forwarding. See * https://docs.microsoft.com/en-us/cpp/extensions/delegate-cpp-component-extensions * for more information. */ @@ -814,9 +843,9 @@ class BuiltInOperationIsDelegate extends BuiltInOperation, @isdelegateexpr { } /** - * The `__is_interface_class` built-in operation. This is a Microsoft extension. + * The `__is_interface_class` built-in operation. This is a Microsoft extension. * - * Returns `true` if the type has been declared as an `interface`. Please see + * Returns `true` if the type has been declared as an `interface`. See * https://docs.microsoft.com/en-us/cpp/extensions/interface-class-cpp-component-extensions * for more information. */ @@ -827,9 +856,9 @@ class BuiltInOperationIsInterfaceClass extends BuiltInOperation, @isinterfacecla } /** - * The `__is_ref_array` built-in operation. This is a Microsoft extension. + * The `__is_ref_array` built-in operation. This is a Microsoft extension. * - * Returns `true` if the object passed in is a _platform array_. Please see + * Returns `true` if the object passed in is a _platform array_. See * https://docs.microsoft.com/en-us/cpp/extensions/arrays-cpp-component-extensions * for more information. * ``` @@ -844,9 +873,9 @@ class BuiltInOperationIsRefArray extends BuiltInOperation, @isrefarrayexpr { } /** - * The `__is_ref_class` built-in operation. This is a Microsoft extension. + * The `__is_ref_class` built-in operation. This is a Microsoft extension. * - * Returns `true` if the type is a _reference class_. Please see + * Returns `true` if the type is a _reference class_. See * https://docs.microsoft.com/en-us/cpp/extensions/classes-and-structs-cpp-component-extensions * for more information. * ``` @@ -861,10 +890,10 @@ class BuiltInOperationIsRefClass extends BuiltInOperation, @isrefclassexpr { } /** - * The `__is_sealed` built-in operation. This is a Microsoft extension. + * The `__is_sealed` built-in operation. This is a Microsoft extension. * * Returns `true` if a given class or virtual function is marked as `sealed`, - * meaning that it cannot be extended or overridden. The `sealed` keyword + * meaning that it cannot be extended or overridden. The `sealed` keyword * is similar to the C++11 `final` keyword. * ``` * ref class X sealed { @@ -879,7 +908,7 @@ class BuiltInOperationIsSealed extends BuiltInOperation, @issealedexpr { } /** - * The `__is_simple_value_class` built-in operation. This is a Microsoft extension. + * The `__is_simple_value_class` built-in operation. This is a Microsoft extension. * * Returns `true` if passed a value type that contains no references to the * garbage-collected heap. @@ -898,9 +927,9 @@ class BuiltInOperationIsSimpleValueClass extends BuiltInOperation, @issimplevalu } /** - * The `__is_value_class` built-in operation. This is a Microsoft extension. + * The `__is_value_class` built-in operation. This is a Microsoft extension. * - * Returns `true` if passed a value type. Please see + * Returns `true` if passed a value type. See * https://docs.microsoft.com/en-us/cpp/extensions/classes-and-structs-cpp-component-extensions * For more information. * ``` @@ -922,7 +951,7 @@ class BuiltInOperationIsValueClass extends BuiltInOperation, @isvalueclassexpr { * ``` * template * struct is_final - * : public integral_constant + * : public integral_constant * { }; * ``` */ @@ -933,7 +962,7 @@ class BuiltInOperationIsFinal extends BuiltInOperation, @isfinalexpr { } /** - * The `__builtin_choose_expr` expression. This is a GNU/Clang extension. + * The `__builtin_choose_expr` expression. This is a gcc/clang extension. * * The expression functions similarly to the ternary `?:` operator, except * that it is evaluated at compile-time. @@ -978,3 +1007,50 @@ class BuiltInComplexOperation extends BuiltInOperation, @builtincomplex { /** Gets the operand corresponding to the imaginary part of the complex number. */ Expr getImaginaryOperand() { this.hasChild(result, 1) } } + +/** + * A C++ `__is_aggregate` built-in operation (used by some implementations of the + * `` header). + * + * Returns `true` if the type has is an aggregate type. + * ``` + * std::integral_constant ia; + * ``` + */ +class BuiltInOperationIsAggregate extends BuiltInOperation, @isaggregate { + override string toString() { result = "__is_aggregate" } + + override string getAPrimaryQlClass() { result = "BuiltInOperationIsAggregate" } +} + +/** + * A C++ `__has_unique_object_representations` built-in operation (used by some + * implementations of the `` header). + * + * Returns `true` if the type is trivially copyable and if the object representation + * is unique for two objects with the same value. + * ``` + * bool v = __has_unique_object_representations(MyType); + * ``` + */ +class BuiltInOperationHasUniqueObjectRepresentations extends BuiltInOperation, + @hasuniqueobjectrepresentations { + override string toString() { result = "__has_unique_object_representations" } + + override string getAPrimaryQlClass() { result = "BuiltInOperationHasUniqueObjectRepresentations" } +} + +/** + * A C/C++ `__builtin_bit_cast` built-in operation (used by some implementations + * of `std::bit_cast`). + * + * Performs a bit cast from a value to a type. + * ``` + * __builtin_bit_cast(Type, value); + * ``` + */ +class BuiltInBitCast extends BuiltInOperation, @builtinbitcast { + override string toString() { result = "__builtin_bit_cast" } + + override string getAPrimaryQlClass() { result = "BuiltInBitCast" } +} diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll index 7ceda8ddbff..dba3d16997f 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Call.qll @@ -255,8 +255,10 @@ class FunctionCall extends Call, @funbindexpr { /** * Gets the function called by this call. * - * In the case of virtual function calls, the result is the most-specific function in the override tree (as - * determined by the compiler) such that the target at runtime will be one of `result.getAnOverridingFunction*()`. + * In the case of virtual function calls, the result is the most-specific function in the override tree + * such that the target at runtime will be one of `result.getAnOverridingFunction*()`. The most-specific + * function is determined by the compiler based on the compile time type of the object the function is a + * member of. */ override Function getTarget() { funbind(underlyingElement(this), unresolveElement(result)) } diff --git a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll index 55a59cc9588..68973293425 100644 --- a/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll +++ b/cpp/ql/lib/semmle/code/cpp/exprs/Expr.qll @@ -596,9 +596,12 @@ class ParenthesisExpr extends Conversion, @parexpr { } /** - * A C/C++ expression that has not been resolved. + * A C/C++ expression that could not be resolved, or that can no longer be + * represented due to a database upgrade or downgrade. * - * It is assigned `ErroneousType` as its type. + * If the expression could not be resolved, it has type `ErroneousType`. In the + * case of a database upgrade or downgrade, the original type from before the + * upgrade or downgrade is kept if that type can be represented. */ class ErrorExpr extends Expr, @errorexpr { override string toString() { result = "" } diff --git a/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll b/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll new file mode 100644 index 00000000000..c11e1457dae --- /dev/null +++ b/cpp/ql/lib/semmle/code/cpp/internal/ResolveGlobalVariable.qll @@ -0,0 +1,57 @@ +private predicate hasDefinition(@globalvariable g) { + exists(@var_decl vd | var_decls(vd, g, _, _, _) | var_def(vd)) +} + +private predicate onlyOneCompleteGlobalVariableExistsWithMangledName(@mangledname name) { + strictcount(@globalvariable g | hasDefinition(g) and mangled_name(g, name)) = 1 +} + +/** Holds if `g` is a unique global variable with a definition named `name`. */ +private predicate isGlobalWithMangledNameAndWithDefinition(@mangledname name, @globalvariable g) { + hasDefinition(g) and + mangled_name(g, name) and + onlyOneCompleteGlobalVariableExistsWithMangledName(name) +} + +/** Holds if `g` is a global variable without a definition named `name`. */ +private predicate isGlobalWithMangledNameAndWithoutDefinition(@mangledname name, @globalvariable g) { + not hasDefinition(g) and + mangled_name(g, name) +} + +/** + * Holds if `incomplete` is a global variable without a definition, and there exists + * a unique global variable `complete` with the same name that does have a definition. + */ +private predicate hasTwinWithDefinition(@globalvariable incomplete, @globalvariable complete) { + exists(@mangledname name | + not variable_instantiation(incomplete, complete) and + isGlobalWithMangledNameAndWithoutDefinition(name, incomplete) and + isGlobalWithMangledNameAndWithDefinition(name, complete) + ) +} + +import Cached + +cached +private module Cached { + /** + * If `v` is a global variable without a definition, and there exists a unique + * global variable with the same name that does have a definition, then the + * result is that unique global variable. Otherwise, the result is `v`. + */ + cached + @variable resolveGlobalVariable(@variable v) { + hasTwinWithDefinition(v, result) + or + not hasTwinWithDefinition(v, _) and + result = v + } + + cached + predicate isVariable(@variable v) { + not v instanceof @globalvariable + or + v = resolveGlobalVariable(_) + } +} diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll index 1f3ea2a4d3d..08ee06acdda 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/MustFlow.qll @@ -38,6 +38,9 @@ abstract class MustFlowConfiguration extends string { */ predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { none() } + /** Holds if this configuration allows flow from arguments to parameters. */ + predicate allowInterproceduralFlow() { any() } + /** * Holds if data must flow from `source` to `sink` for this configuration. * @@ -204,10 +207,25 @@ private module Cached { } } +/** + * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this + * predicate ensures that joins go from `n` to the result instead of the other + * way around. + */ +pragma[inline] +private Declaration getEnclosingCallable(DataFlow::Node n) { + pragma[only_bind_into](result) = pragma[only_bind_out](n).getEnclosingCallable() +} + /** Holds if `nodeFrom` flows to `nodeTo`. */ private predicate step(DataFlow::Node nodeFrom, DataFlow::Node nodeTo, MustFlowConfiguration config) { exists(config) and - Cached::step(nodeFrom, nodeTo) + Cached::step(pragma[only_bind_into](nodeFrom), pragma[only_bind_into](nodeTo)) and + ( + config.allowInterproceduralFlow() + or + getEnclosingCallable(nodeFrom) = getEnclosingCallable(nodeTo) + ) or config.isAdditionalFlowStep(nodeFrom, nodeTo) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..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 @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..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 @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..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 @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..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 @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,7 +3272,7 @@ private predicate parameterCand( DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config ) { exists(ParamNodeEx p | - Stage4::revFlow(p, _, _, _, apa, config) and + Stage4::revFlow(p, _, apa, config) and p.isParameterOf(callable, pos) ) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 9dcd7f176df..e035df824e2 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -244,7 +244,25 @@ OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { * calling context. For example, this would happen with flow through a * global or static variable. */ -predicate jumpStep(Node n1, Node n2) { none() } +predicate jumpStep(Node n1, Node n2) { + exists(GlobalOrNamespaceVariable v | + v = + n1.asInstruction() + .(StoreInstruction) + .getResultAddress() + .(VariableAddressInstruction) + .getAstVariable() and + v = n2.asVariable() + or + v = + n2.asInstruction() + .(LoadInstruction) + .getSourceAddress() + .(VariableAddressInstruction) + .getAstVariable() and + v = n1.asVariable() + ) +} /** * Holds if data can flow from `node1` to `node2` via an assignment to `f`. diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll index 103b5424197..38a886746ab 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedElement.qll @@ -947,7 +947,7 @@ abstract class TranslatedElement extends TTranslatedElement { } /** - * Represents the IR translation of a root element, either a function or a global variable. + * The IR translation of a root element, either a function or a global variable. */ abstract class TranslatedRootElement extends TranslatedElement { TranslatedRootElement() { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll index 31174d8ba5f..dde5e00361a 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/TranslatedGlobalVar.qll @@ -65,7 +65,7 @@ class TranslatedGlobalOrNamespaceVarInit extends TranslatedRootElement, result = this.getInstruction(InitializerVariableAddressTag()) or tag = InitializerVariableAddressTag() and - result = getChild(1).getFirstInstruction() + result = this.getChild(1).getFirstInstruction() or tag = ReturnTag() and result = this.getInstruction(AliasedUseTag()) diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll index c9e790d9077..0f47770dc82 100644 --- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll +++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/SimpleRangeAnalysis.qll @@ -1573,7 +1573,7 @@ private module SimpleRangeAnalysisCached { result = min([max(getTruncatedUpperBounds(expr)), getGuardedUpperBound(expr)]) } - /** Holds if the upper bound of `expr` may have been widened. This means the the upper bound is in practice likely to be overly wide. */ + /** Holds if the upper bound of `expr` may have been widened. This means the upper bound is in practice likely to be overly wide. */ cached predicate upperBoundMayBeWidened(Expr e) { isRecursiveExpr(e) and diff --git a/cpp/ql/lib/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 2b404ff5288..ae7e4f7151b 100644 --- a/cpp/ql/src/CHANGELOG.md +++ b/cpp/ql/src/CHANGELOG.md @@ -1,3 +1,11 @@ +## 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 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 bd55008677c..ed1d4084993 100644 --- a/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql +++ b/cpp/ql/src/Likely Bugs/Memory Management/ReturnStackAllocatedMemory.ql @@ -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/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/2022-06-29-move-contextual-queries.md b/cpp/ql/src/change-notes/released/0.3.0.md similarity index 77% rename from cpp/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename to cpp/ql/src/change-notes/released/0.3.0.md index cc5464d58b3..75d99f333c9 100644 --- a/cpp/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ b/cpp/ql/src/change-notes/released/0.3.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 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 5274e27ed52..bb106b1cb63 100644 --- a/cpp/ql/src/codeql-pack.release.yml +++ b/cpp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.1 diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.cpp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.cpp new file mode 100644 index 00000000000..3b71c4f3005 --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.cpp @@ -0,0 +1,6 @@ + +... +mbtowc(&wc, ptr, 4)); // BAD:we can get unpredictable results +... +mbtowc(&wc, ptr, MB_LEN_MAX); // GOOD +... diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp new file mode 100644 index 00000000000..e6f18c5c8ae --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.qhelp @@ -0,0 +1,23 @@ + + + +

Using a function to convert multibyte or wide characters with an invalid length argument may result in an out-of-range access error or unexpected results.

+ +
+ + +

The following example shows the erroneous and corrected method of using function mbtowc.

+ + +
+ + +
  • + CERT Coding Standard: + ARR30-C. Do not form or use out-of-bounds pointers or array subscripts - SEI CERT C Coding Standard - Confluence. +
  • + +
    +
    diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql new file mode 100644 index 00000000000..f73ba21c39b --- /dev/null +++ b/cpp/ql/src/experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql @@ -0,0 +1,236 @@ +/** + * @name Dangerous use convert function. + * @description Using convert function with an invalid length argument can result in an out-of-bounds access error or unexpected result. + * @kind problem + * @id cpp/dangerous-use-convert-function + * @problem.severity warning + * @precision medium + * @tags correctness + * security + * external/cwe/cwe-125 + */ + +import cpp +import semmle.code.cpp.valuenumbering.GlobalValueNumbering + +/** Holds if there are indications that the variable is treated as a string. */ +predicate exprMayBeString(Expr exp) { + ( + exists(StringLiteral sl | globalValueNumber(exp) = globalValueNumber(sl)) + or + exists(FunctionCall fctmp | + ( + fctmp.getAnArgument().(VariableAccess).getTarget() = exp.(VariableAccess).getTarget() or + globalValueNumber(fctmp.getAnArgument()) = globalValueNumber(exp) + ) and + fctmp.getTarget().hasName(["strlen", "strcat", "strncat", "strcpy", "sptintf", "printf"]) + ) + or + exists(AssignExpr astmp | + astmp.getRValue().getValue() = "0" and + astmp.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = + exp.(VariableAccess).getTarget() + ) + or + exists(ComparisonOperation cotmp, Expr exptmp1, Expr exptmp2 | + exptmp1.getValue() = "0" and + ( + exptmp2.(PointerDereferenceExpr).getOperand().(VariableAccess).getTarget() = + exp.(VariableAccess).getTarget() or + exptmp2.(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = + exp.getAChild().(VariableAccess).getTarget() + ) and + cotmp.hasOperands(exptmp1, exptmp2) + ) + ) +} + +/** Holds if expression is constant or operator call `sizeof`. */ +predicate argConstOrSizeof(Expr exp) { + exp.getValue().toInt() > 1 or + exp.(SizeofTypeOperator).getTypeOperand().getSize() > 1 +} + +/** Holds if expression is macro. */ +predicate argMacro(Expr exp) { + exists(MacroInvocation matmp | + exp = matmp.getExpr() and + ( + matmp.getMacroName() = "MB_LEN_MAX" or + matmp.getMacroName() = "MB_CUR_MAX" + ) + ) +} + +/** Holds if erroneous situations of using functions `mbtowc` and `mbrtowc` are detected. */ +predicate findUseCharacterConversion(Expr exp, string msg) { + exists(FunctionCall fc | + fc = exp and + ( + exists(Loop lptmp | lptmp = fc.getEnclosingStmt().getParentStmt*()) and + fc.getTarget().hasName(["mbtowc", "mbrtowc", "_mbtowc_l"]) and + not fc.getArgument(0).isConstant() and + not fc.getArgument(1).isConstant() and + ( + exprMayBeString(fc.getArgument(1)) and + argConstOrSizeof(fc.getArgument(2)) and + fc.getArgument(2).getValue().toInt() < 5 and + not argMacro(fc.getArgument(2)) and + msg = "Size can be less than maximum character length, use macro MB_CUR_MAX." + or + not exprMayBeString(fc.getArgument(1)) and + ( + argConstOrSizeof(fc.getArgument(2)) + or + argMacro(fc.getArgument(2)) + or + exists(DecrementOperation dotmp | + globalValueNumber(dotmp.getAnOperand()) = globalValueNumber(fc.getArgument(2)) and + not exists(AssignSubExpr aetmp | + ( + aetmp.getLValue().(VariableAccess).getTarget() = + fc.getArgument(2).(VariableAccess).getTarget() or + globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(2)) + ) and + globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc) + ) + ) + ) and + msg = + "Access beyond the allocated memory is possible, the length can change without changing the pointer." + or + exists(AssignPointerAddExpr aetmp | + ( + aetmp.getLValue().(VariableAccess).getTarget() = + fc.getArgument(0).(VariableAccess).getTarget() or + globalValueNumber(aetmp.getLValue()) = globalValueNumber(fc.getArgument(0)) + ) and + globalValueNumber(aetmp.getRValue()) = globalValueNumber(fc) + ) and + msg = "Maybe you're using the function's return value incorrectly." + ) + ) + ) +} + +/** Holds if detecting erroneous situations of working with multibyte characters. */ +predicate findUseMultibyteCharacter(Expr exp, string msg) { + exists(ArrayType arrayType, ArrayExpr arrayExpr | + arrayExpr = exp and + arrayExpr.getArrayBase().getType() = arrayType and + ( + exists(AssignExpr assZero, SizeofExprOperator sizeofArray, Expr oneValue | + oneValue.getValue() = "1" and + sizeofArray.getExprOperand().getType() = arrayType and + assZero.getLValue() = arrayExpr and + arrayExpr.getArrayOffset().(SubExpr).hasOperands(sizeofArray, oneValue) and + assZero.getRValue().getValue() = "0" + ) and + arrayType.getArraySize() != arrayType.getByteSize() and + msg = + "The size of the array element is greater than one byte, so the offset will point outside the array." + or + exists(FunctionCall mbFunction | + ( + mbFunction.getTarget().getName().matches("_mbs%") or + mbFunction.getTarget().getName().matches("mbs%") or + mbFunction.getTarget().getName().matches("_mbc%") or + mbFunction.getTarget().getName().matches("mbc%") + ) and + mbFunction.getAnArgument().(VariableAccess).getTarget().getADeclarationEntry().getType() = + arrayType + ) and + exists(Loop loop, SizeofExprOperator sizeofArray, AssignExpr assignExpr | + arrayExpr.getEnclosingStmt().getParentStmt*() = loop and + sizeofArray.getExprOperand().getType() = arrayType and + assignExpr.getLValue() = arrayExpr and + loop.getCondition().(LTExpr).getLeftOperand().(VariableAccess).getTarget() = + arrayExpr.getArrayOffset().getAChild*().(VariableAccess).getTarget() and + loop.getCondition().(LTExpr).getRightOperand() = sizeofArray + ) and + msg = + "This buffer may contain multibyte characters, so attempting to copy may result in part of the last character being lost." + ) + ) + or + exists(FunctionCall mbccpy, Loop loop, SizeofExprOperator sizeofOp | + mbccpy.getTarget().hasName("_mbccpy") and + mbccpy.getArgument(0) = exp and + exp.getEnclosingStmt().getParentStmt*() = loop and + sizeofOp.getExprOperand().getType() = + exp.getAChild*().(VariableAccess).getTarget().getADeclarationEntry().getType() and + loop.getCondition().(LTExpr).getLeftOperand().(VariableAccess).getTarget() = + exp.getAChild*().(VariableAccess).getTarget() and + loop.getCondition().(LTExpr).getRightOperand() = sizeofOp and + msg = + "This buffer may contain multibyte characters, so an attempt to copy may result in an overflow." + ) +} + +/** Holds if erroneous situations of using functions `MultiByteToWideChar` and `WideCharToMultiByte` or `mbstowcs` and `_mbstowcs_l` and `mbsrtowcs` are detected. */ +predicate findUseStringConversion( + Expr exp, string msg, int posBufSrc, int posBufDst, int posSizeDst, string nameCalls +) { + exists(FunctionCall fc | + fc = exp and + posBufSrc in [0 .. fc.getNumberOfArguments() - 1] and + posSizeDst in [0 .. fc.getNumberOfArguments() - 1] and + ( + fc.getTarget().hasName(nameCalls) and + ( + globalValueNumber(fc.getArgument(posBufDst)) = globalValueNumber(fc.getArgument(posBufSrc)) and + msg = + "According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible." + or + exists(ArrayType arrayDst | + fc.getArgument(posBufDst).(VariableAccess).getTarget().getADeclarationEntry().getType() = + arrayDst and + fc.getArgument(posSizeDst).getValue().toInt() >= arrayDst.getArraySize() and + not exists(AssignExpr assZero | + assZero.getLValue().(ArrayExpr).getArrayBase().(VariableAccess).getTarget() = + fc.getArgument(posBufDst).(VariableAccess).getTarget() and + assZero.getRValue().getValue() = "0" + ) and + not exists(Expr someExp, FunctionCall checkSize | + checkSize.getASuccessor*() = fc and + checkSize.getTarget().hasName(nameCalls) and + checkSize.getArgument(posSizeDst).getValue() = "0" and + globalValueNumber(checkSize) = globalValueNumber(someExp) and + someExp.getEnclosingStmt().getParentStmt*() instanceof IfStmt + ) and + exprMayBeString(fc.getArgument(posBufDst)) and + msg = + "According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible." + ) + or + exists(FunctionCall allocMem | + allocMem.getTarget().hasName(["calloc", "malloc"]) and + globalValueNumber(fc.getArgument(posBufDst)) = globalValueNumber(allocMem) and + ( + allocMem.getArgument(allocMem.getNumberOfArguments() - 1).getValue() = "1" or + not exists(SizeofOperator sizeofOperator | + globalValueNumber(allocMem + .getArgument(allocMem.getNumberOfArguments() - 1) + .getAChild*()) = globalValueNumber(sizeofOperator) + ) + ) and + msg = + "The buffer destination has a type other than char, you need to take this into account when allocating memory." + ) + or + fc.getArgument(posBufDst).getValue() = "0" and + fc.getArgument(posSizeDst).getValue() != "0" and + msg = + "If the destination buffer is NULL and its size is not 0, then undefined behavior is possible." + ) + ) + ) +} + +from Expr exp, string msg +where + findUseCharacterConversion(exp, msg) or + findUseMultibyteCharacter(exp, msg) or + findUseStringConversion(exp, msg, 1, 0, 2, ["mbstowcs", "_mbstowcs_l", "mbsrtowcs"]) or + findUseStringConversion(exp, msg, 2, 4, 5, ["MultiByteToWideChar", "WideCharToMultiByte"]) +select exp, msg diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp b/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp index ca8d8dfaf22..1daebb58b3c 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp +++ b/cpp/ql/src/experimental/Security/CWE/CWE-273/PrivilegeDroppingOutoforder.qhelp @@ -27,6 +27,9 @@ groups, and finally set the target user.

    +
  • CERT C Coding Standard: +POS36-C. Observe correct revocation order while relinquishing privileges. +
  • CERT C Coding Standard: POS37-C. Ensure that privilege relinquishment is successful.
  • diff --git a/cpp/ql/src/qlpack.yml b/cpp/ql/src/qlpack.yml index 62cac967801..03b90cb3668 100644 --- a/cpp/ql/src/qlpack.yml +++ b/cpp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/cpp-queries -version: 0.2.1-dev +version: 0.3.2-dev groups: - cpp - queries diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.expected new file mode 100644 index 00000000000..6766b6c46a4 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.expected @@ -0,0 +1,26 @@ +| test1.cpp:28:5:28:23 | call to WideCharToMultiByte | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. | +| test1.cpp:29:5:29:23 | call to MultiByteToWideChar | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. | +| test1.cpp:45:3:45:21 | call to WideCharToMultiByte | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. | +| test1.cpp:58:3:58:21 | call to MultiByteToWideChar | The buffer destination has a type other than char, you need to take this into account when allocating memory. | +| test1.cpp:70:3:70:21 | call to MultiByteToWideChar | The buffer destination has a type other than char, you need to take this into account when allocating memory. | +| test1.cpp:76:10:76:28 | call to WideCharToMultiByte | If the destination buffer is NULL and its size is not 0, then undefined behavior is possible. | +| test1.cpp:93:5:93:23 | call to WideCharToMultiByte | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. | +| test2.cpp:15:5:15:12 | call to mbstowcs | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. | +| test2.cpp:17:5:17:15 | call to _mbstowcs_l | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. | +| test2.cpp:19:5:19:13 | call to mbsrtowcs | According to the definition of the functions, if the source buffer and the destination buffer are the same, undefined behavior is possible. | +| test2.cpp:35:3:35:10 | call to mbstowcs | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. | +| test2.cpp:48:3:48:10 | call to mbstowcs | The buffer destination has a type other than char, you need to take this into account when allocating memory. | +| test2.cpp:60:3:60:10 | call to mbstowcs | The buffer destination has a type other than char, you need to take this into account when allocating memory. | +| test2.cpp:66:10:66:17 | call to mbstowcs | If the destination buffer is NULL and its size is not 0, then undefined behavior is possible. | +| test2.cpp:80:3:80:10 | call to mbstowcs | According to the definition of the functions, it is not guaranteed to write a null character at the end of the string, so access beyond the bounds of the destination buffer is possible. | +| test3.cpp:16:5:16:13 | access to array | This buffer may contain multibyte characters, so attempting to copy may result in part of the last character being lost. | +| test3.cpp:36:13:36:18 | ... + ... | This buffer may contain multibyte characters, so an attempt to copy may result in an overflow. | +| test3.cpp:47:3:47:24 | access to array | The size of the array element is greater than one byte, so the offset will point outside the array. | +| test.cpp:66:27:66:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | +| test.cpp:76:27:76:32 | call to mbtowc | Size can be less than maximum character length, use macro MB_CUR_MAX. | +| test.cpp:106:11:106:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:123:11:123:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:140:11:140:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:158:11:158:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:181:11:181:16 | call to mbtowc | Access beyond the allocated memory is possible, the length can change without changing the pointer. | +| test.cpp:197:11:197:16 | call to mbtowc | Maybe you're using the function's return value incorrectly. | diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.qlref b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.qlref new file mode 100644 index 00000000000..228684a4e25 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/DangerousWorksWithMultibyteOrWideCharacters.qlref @@ -0,0 +1 @@ +experimental/Security/CWE/CWE-125/DangerousWorksWithMultibyteOrWideCharacters.ql diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp new file mode 100644 index 00000000000..b5e8096af2a --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test.cpp @@ -0,0 +1,206 @@ +typedef unsigned long size_t; +#define MB_CUR_MAX 6 +#define MB_LEN_MAX 16 +int mbtowc(wchar_t *out, const char *in, size_t size); +int wprintf (const wchar_t* format, ...); +int strlen( const char * string ); +int checkErrors(); + +void goodTest0() +{ + char * ptr = "123456789"; + int ret; + int len; + len = 9; + for (wchar_t wc; (ret = mbtowc(&wc, ptr, len)) > 0; len-=ret) { // GOOD + wprintf(L"%lc", wc); + ptr += ret; + } +} +void goodTest1(const char* ptr) +{ + int ret; + int len; + len = strlen(ptr); + for (wchar_t wc; (ret = mbtowc(&wc, ptr, len)) > 0; len-=ret) { // GOOD + wprintf(L"%lc", wc); + ptr += ret; + } +} +void goodTest2(char* ptr) +{ + int ret; + ptr[10]=0; + int len = 9; + for (wchar_t wc; (ret = mbtowc(&wc, ptr, 16)) > 0; len-=ret) { // GOOD + wprintf(L"%lc", wc); + ptr += ret; + } +} + +void goodTest3(const char* ptr) +{ + int ret; + int len; + len = strlen(ptr); + for (wchar_t wc; (ret = mbtowc(&wc, ptr, MB_CUR_MAX)) > 0; len-=ret) { // GOOD + wprintf(L"%lc", wc); + ptr += ret; + } +} +void goodTest4(const char* ptr) +{ + int ret; + int len; + len = strlen(ptr); + for (wchar_t wc; (ret = mbtowc(&wc, ptr, MB_LEN_MAX)) > 0; len-=ret) { // GOOD + wprintf(L"%lc", wc); + ptr += ret; + } +} +void badTest1(const char* ptr) +{ + int ret; + int len; + len = strlen(ptr); + for (wchar_t wc; (ret = mbtowc(&wc, ptr, 4)) > 0; len-=ret) { // BAD:we can get unpredictable results + wprintf(L"%lc", wc); + ptr += ret; + } +} +void badTest2(const char* ptr) +{ + int ret; + int len; + len = strlen(ptr); + for (wchar_t wc; (ret = mbtowc(&wc, ptr, sizeof(wchar_t))) > 0; len-=ret) { // BAD:we can get unpredictable results + wprintf(L"%lc", wc); + ptr += ret; + } +} + +void goodTest5(const char* ptr,wchar_t *wc,int wc_len) +{ + int ret; + int len; + len = wc_len; + while (*ptr && len > 0) { + ret = mbtowc(wc, ptr, len); // GOOD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len-=ret; + ptr+=ret; + wc++; + } +} + +void badTest3(const char* ptr,int wc_len) +{ + int ret; + int len; + len = wc_len; + wchar_t *wc = new wchar_t[wc_len]; + while (*ptr && len > 0) { + ret = mbtowc(wc, ptr, MB_CUR_MAX); // BAD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len-=ret; + ptr+=ret; + wc++; + } +} +void badTest4(const char* ptr,int wc_len) +{ + int ret; + int len; + len = wc_len; + wchar_t *wc = new wchar_t[wc_len]; + while (*ptr && len > 0) { + ret = mbtowc(wc, ptr, 16); // BAD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len-=ret; + ptr+=ret; + wc++; + } +} +void badTest5(const char* ptr,int wc_len) +{ + int ret; + int len; + len = wc_len; + wchar_t *wc = new wchar_t[wc_len]; + while (*ptr && len > 0) { + ret = mbtowc(wc, ptr, sizeof(wchar_t)); // BAD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len-=ret; + ptr+=ret; + wc++; + } +} + +void badTest6(const char* ptr,int wc_len) +{ + int ret; + int len; + len = wc_len; + wchar_t *wc = new wchar_t[wc_len]; + while (*ptr && wc_len > 0) { + ret = mbtowc(wc, ptr, wc_len); // BAD + if (ret <0) + if (checkErrors()) { + ++ptr; + --len; + continue; + } else + break; + if (ret == 0 || ret > len) + break; + wc_len--; + len-=ret; + wc++; + ptr+=ret; + } +} +void badTest7(const char* ptr,int wc_len) +{ + int ret; + int len; + len = wc_len; + wchar_t *wc = new wchar_t[wc_len]; + while (*ptr && wc_len > 0) { + ret = mbtowc(wc, ptr, len); // BAD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len--; + wc++; + ptr+=ret; + } +} +void badTest8(const char* ptr,wchar_t *wc) +{ + int ret; + int len; + len = strlen(ptr); + while (*ptr && len > 0) { + ret = mbtowc(wc, ptr, len); // BAD + if (ret <0) + break; + if (ret == 0 || ret > len) + break; + len-=ret; + ptr++; + wc+=ret; + } +} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test1.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test1.cpp new file mode 100644 index 00000000000..828b91a44f1 --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test1.cpp @@ -0,0 +1,95 @@ +#define CP_ACP 1 +#define CP_UTF8 1 +#define WC_COMPOSITECHECK 1 +#define NULL 0 +typedef unsigned int UINT; +typedef unsigned long DWORD, *PDWORD, *LPDWORD; +typedef char CHAR; +#define CONST const +typedef wchar_t WCHAR; + +typedef CHAR *LPSTR; +typedef CONST CHAR *LPCSTR; +typedef CONST WCHAR *LPCWSTR; + +typedef int BOOL; +typedef BOOL *LPBOOL; + + +int WideCharToMultiByte(UINT CodePage,DWORD dwFlags,LPCWSTR lpWideCharStr,int cchWideChar,LPSTR lpMultiByteStr,int cbMultiByte,LPCWSTR lpDefaultChar,LPBOOL lpUsedDefaultChar); +int MultiByteToWideChar(UINT CodePage,DWORD dwFlags,LPCSTR lpMultiByteStr,int cbMultiByte,LPCWSTR lpWideCharStr,int cchWideChar); + +int printf ( const char * format, ... ); +typedef unsigned int size_t; +void* calloc (size_t num, size_t size); +void* malloc (size_t size); + +void badTest1(void *src, int size) { + WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, (LPSTR)src, size, 0, 0); // BAD + MultiByteToWideChar(CP_ACP, 0, (LPCSTR)src, -1, (LPCWSTR)src, 30); // BAD +} +void goodTest2(){ + wchar_t src[] = L"0123456789ABCDEF"; + char dst[16]; + int res = WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, 16, NULL, NULL); // GOOD + if (res == sizeof(dst)) { + dst[res-1] = NULL; + } else { + dst[res] = NULL; + } + printf("%s\n", dst); +} +void badTest2(){ + wchar_t src[] = L"0123456789ABCDEF"; + char dst[16]; + WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, 16, NULL, NULL); // BAD + printf("%s\n", dst); +} +void goodTest3(){ + char src[] = "0123456789ABCDEF"; + int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0); + wchar_t * dst = (wchar_t*)calloc(size + 1, sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // GOOD +} +void badTest3(){ + char src[] = "0123456789ABCDEF"; + int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0); + wchar_t * dst = (wchar_t*)calloc(size + 1, 1); + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // BAD +} +void goodTest4(){ + char src[] = "0123456789ABCDEF"; + int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0); + wchar_t * dst = (wchar_t*)malloc((size + 1)*sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // GOOD +} +void badTest4(){ + char src[] = "0123456789ABCDEF"; + int size = MultiByteToWideChar(CP_UTF8, 0, src,sizeof(src),NULL,0); + wchar_t * dst = (wchar_t*)malloc(size + 1); + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, size+1); // BAD +} +int goodTest5(void *src){ + return WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, 0, 0, 0, 0); // GOOD +} +int badTest5 (void *src) { + return WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)src, -1, 0, 3, 0, 0); // BAD +} +void goodTest6(WCHAR *src) +{ + int size; + char dst[5] =""; + size = WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK, src, -1, dst, 0, 0, 0); + if(size>=sizeof(dst)){ + printf("buffer size error\n"); + return; + } + WideCharToMultiByte(CP_ACP, 0, src, -1, dst, sizeof(dst), 0, 0); // GOOD + printf("%s\n", dst); +} +void badTest6(WCHAR *src) +{ + char dst[5] =""; + WideCharToMultiByte(CP_ACP, 0, src, -1, dst, 260, 0, 0); // BAD + printf("%s\n", dst); +} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test2.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test2.cpp new file mode 100644 index 00000000000..99dc3e47e5b --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test2.cpp @@ -0,0 +1,82 @@ +#define NULL 0 +typedef unsigned int size_t; +struct mbstate_t{}; +struct _locale_t{}; +int printf ( const char * format, ... ); +void* calloc (size_t num, size_t size); +void* malloc (size_t size); + +size_t mbstowcs(wchar_t *wcstr,const char *mbstr,size_t count); +size_t _mbstowcs_l(wchar_t *wcstr,const char *mbstr,size_t count, _locale_t locale); +size_t mbsrtowcs(wchar_t *wcstr,const char *mbstr,size_t count, mbstate_t *mbstate); + + +void badTest1(void *src, int size) { + mbstowcs((wchar_t*)src,(char*)src,size); // BAD + _locale_t locale; + _mbstowcs_l((wchar_t*)src,(char*)src,size,locale); // BAD + mbstate_t *mbstate; + mbsrtowcs((wchar_t*)src,(char*)src,size,mbstate); // BAD +} +void goodTest2(){ + char src[] = "0123456789ABCDEF"; + wchar_t dst[16]; + int res = mbstowcs(dst, src,16); // GOOD + if (res == sizeof(dst)) { + dst[res-1] = NULL; + } else { + dst[res] = NULL; + } + printf("%s\n", dst); +} +void badTest2(){ + char src[] = "0123456789ABCDEF"; + wchar_t dst[16]; + mbstowcs(dst, src,16); // BAD + printf("%s\n", dst); +} +void goodTest3(){ + char src[] = "0123456789ABCDEF"; + int size = mbstowcs(NULL, src,NULL); + wchar_t * dst = (wchar_t*)calloc(size + 1, sizeof(wchar_t)); + mbstowcs(dst, src,size+1); // GOOD +} +void badTest3(){ + char src[] = "0123456789ABCDEF"; + int size = mbstowcs(NULL, src,NULL); + wchar_t * dst = (wchar_t*)calloc(size + 1, 1); + mbstowcs(dst, src,size+1); // BAD +} +void goodTest4(){ + char src[] = "0123456789ABCDEF"; + int size = mbstowcs(NULL, src,NULL); + wchar_t * dst = (wchar_t*)malloc((size + 1)*sizeof(wchar_t)); + mbstowcs(dst, src,size+1); // GOOD +} +void badTest4(){ + char src[] = "0123456789ABCDEF"; + int size = mbstowcs(NULL, src,NULL); + wchar_t * dst = (wchar_t*)malloc(size + 1); + mbstowcs(dst, src,size+1); // BAD +} +int goodTest5(void *src){ + return mbstowcs(NULL, (char*)src,NULL); // GOOD +} +int badTest5 (void *src) { + return mbstowcs(NULL, (char*)src,3); // BAD +} +void goodTest6(void *src){ + wchar_t dst[5]; + int size = mbstowcs(NULL, (char*)src,NULL); + if(size>=sizeof(dst)){ + printf("buffer size error\n"); + return; + } + mbstowcs(dst, (char*)src,sizeof(dst)); // GOOD + printf("%s\n", dst); +} +void badTest6(void *src){ + wchar_t dst[5]; + mbstowcs(dst, (char*)src,260); // BAD + printf("%s\n", dst); +} diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test3.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test3.cpp new file mode 100644 index 00000000000..e37052e839b --- /dev/null +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-125/semmle/tests/test3.cpp @@ -0,0 +1,48 @@ +#define NULL 0 +typedef unsigned int size_t; + +unsigned char * _mbsnbcpy(unsigned char * strDest,const unsigned char * strSource,size_t count); +size_t _mbclen(const unsigned char *c); +void _mbccpy(unsigned char *dest,const unsigned char *src); +unsigned char *_mbsinc(const unsigned char *current); +void goodTest1(unsigned char *src){ + unsigned char dst[50]; + _mbsnbcpy(dst,src,sizeof(dst)); // GOOD +} +size_t badTest1(unsigned char *src){ + int cb = 0; + unsigned char dst[50]; + while( cb < sizeof(dst) ) + dst[cb++]=*src++; // BAD + return _mbclen(dst); +} +void goodTest2(unsigned char *src){ + + int cb = 0; + unsigned char dst[50]; + while( (cb + _mbclen(src)) <= sizeof(dst) ) + { + _mbccpy(dst+cb,src); // GOOD + cb+=_mbclen(src); + src=_mbsinc(src); + } +} +void badTest2(unsigned char *src){ + + int cb = 0; + unsigned char dst[50]; + while( cb < sizeof(dst) ) + { + _mbccpy(dst+cb,src); // BAD + cb+=_mbclen(src); + src=_mbsinc(src); + } +} +void goodTest3(){ + wchar_t name[50]; + name[sizeof(name) / sizeof(*name) - 1] = L'\0'; // GOOD +} +void badTest3(){ + wchar_t name[50]; + name[sizeof(name) - 1] = L'\0'; // BAD +} diff --git a/cpp/ql/test/library-tests/builtins/edg/edg.c b/cpp/ql/test/library-tests/builtins/edg/edg.c index d1a78435317..f1f0f0f1375 100644 --- a/cpp/ql/test/library-tests/builtins/edg/edg.c +++ b/cpp/ql/test/library-tests/builtins/edg/edg.c @@ -1,4 +1,4 @@ - +// semmle-extractor-options: --clang struct mystruct { int f1; int f2; @@ -13,3 +13,6 @@ void f(void) { int i2 = edg_offsetof(struct mystruct,f2); } +void g(void) { + double f = __builtin_bit_cast(double,42l); +} diff --git a/cpp/ql/test/library-tests/builtins/edg/expr.expected b/cpp/ql/test/library-tests/builtins/edg/expr.expected index 0969dc1e217..5ab0747ecc7 100644 --- a/cpp/ql/test/library-tests/builtins/edg/expr.expected +++ b/cpp/ql/test/library-tests/builtins/edg/expr.expected @@ -13,3 +13,6 @@ | edg.c:13:14:13:45 | (size_t)... | 0 | 0 | | edg.c:13:14:13:45 | __INTADDR__ | 1 | 1 | | edg.c:13:43:13:44 | f2 | 0 | 0 | +| edg.c:17:16:17:45 | __builtin_bit_cast | 1 | 1 | +| edg.c:17:16:17:45 | double | 0 | 0 | +| edg.c:17:42:17:44 | 42 | 1 | 1 | diff --git a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected index 47918496198..a19d917aaac 100644 --- a/cpp/ql/test/library-tests/builtins/type_traits/expr.expected +++ b/cpp/ql/test/library-tests/builtins/type_traits/expr.expected @@ -296,3 +296,20 @@ | ms.cpp:255:24:255:43 | a_struct | | | | ms.cpp:256:24:256:49 | __is_final | a_final_struct | 1 | | ms.cpp:256:24:256:49 | a_final_struct | | | +| ms.cpp:258:29:258:62 | __is_assignable | a_struct,a_struct | 1 | +| ms.cpp:258:29:258:62 | a_struct | | | +| ms.cpp:258:29:258:62 | a_struct | | | +| ms.cpp:259:29:259:59 | __is_assignable | a_struct,empty | 0 | +| ms.cpp:259:29:259:59 | a_struct | | | +| ms.cpp:259:29:259:59 | empty | | | +| ms.cpp:260:29:260:57 | __is_assignable | a_struct,int | 0 | +| ms.cpp:260:29:260:57 | a_struct | | | +| ms.cpp:260:29:260:57 | int | | | +| ms.cpp:262:28:262:51 | __is_aggregate | a_struct | 1 | +| ms.cpp:262:28:262:51 | a_struct | | | +| ms.cpp:263:28:263:46 | __is_aggregate | int | 0 | +| ms.cpp:263:28:263:46 | int | | | +| ms.cpp:265:49:265:88 | __has_unique_object_representations | int | 1 | +| ms.cpp:265:49:265:88 | int | | | +| ms.cpp:266:49:266:90 | __has_unique_object_representations | float | 0 | +| ms.cpp:266:49:266:90 | float | | | diff --git a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp index 742f95faf07..91d6245cc35 100644 --- a/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp +++ b/cpp/ql/test/library-tests/builtins/type_traits/ms.cpp @@ -254,5 +254,14 @@ void f(void) { bool b_is_final1 = __is_final(a_struct); bool b_is_final2 = __is_final(a_final_struct); -} + bool b_is_assignable1 = __is_assignable(a_struct,a_struct); + bool b_is_assignable2 = __is_assignable(a_struct,empty); + bool b_is_assignable3 = __is_assignable(a_struct,int); + + bool b_is_aggregate1 = __is_aggregate(a_struct); + bool b_is_aggregate2 = __is_aggregate(int); + + bool b_has_unique_object_representations1 = __has_unique_object_representations(int); + bool b_has_unique_object_representations2 = __has_unique_object_representations(float); +} diff --git a/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected b/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected new file mode 100644 index 00000000000..bcf301ba47b --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/nullness/nullness.expected @@ -0,0 +1,20 @@ +| test.cpp:9:9:9:9 | v | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:10:9:10:10 | ! ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:11:9:11:14 | ... == ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:12:9:12:17 | ... == ... | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:13:9:13:14 | ... != ... | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:14:9:14:17 | ... != ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:15:8:15:23 | call to __builtin_expect | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:16:8:16:23 | call to __builtin_expect | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:17:9:17:17 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:18:9:18:17 | ... && ... | test.cpp:5:13:5:13 | v | is not null | is valid | +| test.cpp:19:9:19:18 | ... && ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:20:9:20:18 | ... && ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:21:9:21:14 | ... = ... | test.cpp:5:13:5:13 | v | is null | is not valid | +| test.cpp:21:9:21:14 | ... = ... | test.cpp:7:10:7:10 | b | is not null | is valid | +| test.cpp:22:9:22:14 | ... = ... | test.cpp:5:13:5:13 | v | is not null | is not valid | +| test.cpp:22:9:22:14 | ... = ... | test.cpp:7:13:7:13 | c | is not null | is not valid | +| test.cpp:22:17:22:17 | c | test.cpp:7:13:7:13 | c | is not null | is valid | +| test.cpp:23:21:23:21 | x | test.cpp:23:14:23:14 | x | is not null | is valid | +| test.cpp:24:9:24:18 | (condition decl) | test.cpp:5:13:5:13 | v | is not null | is not valid | +| test.cpp:24:9:24:18 | (condition decl) | test.cpp:24:14:24:14 | y | is not null | is valid | diff --git a/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql b/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql new file mode 100644 index 00000000000..864fd04f920 --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/nullness/nullness.ql @@ -0,0 +1,8 @@ +import cpp + +from AnalysedExpr a, LocalScopeVariable v, string isNullCheck, string isValidCheck +where + v.getAnAccess().getEnclosingStmt() = a.getParent() and + (if a.isNullCheck(v) then isNullCheck = "is null" else isNullCheck = "is not null") and + (if a.isValidCheck(v) then isValidCheck = "is valid" else isValidCheck = "is not valid") +select a, v, isNullCheck, isValidCheck diff --git a/cpp/ql/test/library-tests/controlflow/nullness/test.cpp b/cpp/ql/test/library-tests/controlflow/nullness/test.cpp new file mode 100644 index 00000000000..407753be17a --- /dev/null +++ b/cpp/ql/test/library-tests/controlflow/nullness/test.cpp @@ -0,0 +1,25 @@ +// semmle-extractor-options: -std=c++17 + +long __builtin_expect(long); + +void f(int *v) { + int *w; + bool b, c; + + if (v) {} + if (!v) {} + if (v == 0) {} + if ((!v) == 0) {} + if (v != 0) {} + if ((!v) != 0) {} + if(__builtin_expect((long)v)) {} + if(__builtin_expect((long)!v)) {} + if (true && v) {} + if (v && true) {} + if (true && !v) {} + if (!v && true) {} + if (b = !v) {} + if (c = !v; c) {} + if (int *x = v; x) {} + if (int *y = v) {} +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp index 5e5c5279f16..18eb893e540 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp @@ -334,19 +334,19 @@ namespace FlowThroughGlobals { } int f() { - sink(globalVar); // tainted or clean? Not sure. + sink(globalVar); // $ ir=333:17 ir=347:17 // tainted or clean? Not sure. taintGlobal(); - sink(globalVar); // $ MISSING: ast,ir + sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast } int calledAfterTaint() { - sink(globalVar); // $ MISSING: ast,ir + sink(globalVar); // $ ir=333:17 ir=347:17 MISSING: ast } int taintAndCall() { globalVar = source(); calledAfterTaint(); - sink(globalVar); // $ ast,ir + sink(globalVar); // $ ast ir=333:17 ir=347:17 } } diff --git a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp index ebbde802ff3..2ae093098d2 100644 --- a/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp +++ b/cpp/ql/test/library-tests/dataflow/taint-tests/taint.cpp @@ -47,14 +47,14 @@ void do_source() void do_sink() { sink(global1); - sink(global2); // $ MISSING: ast,ir - sink(global3); // $ MISSING: ast,ir - sink(global4); // $ MISSING: ast,ir + sink(global2); // $ ir MISSING: ast + sink(global3); // $ ir MISSING: ast + sink(global4); // $ ir MISSING: ast sink(global5); sink(global6); - sink(global7); // $ MISSING: ast,ir - sink(global8); // $ MISSING: ast,ir - sink(global9); // $ MISSING: ast,ir + sink(global7); // $ ir MISSING: ast + sink(global8); // $ ir MISSING: ast + sink(global9); // $ ir MISSING: ast sink(global10); } diff --git a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll index a9167597691..bd77d831cb7 100644 --- a/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll +++ b/cpp/ql/test/library-tests/ir/ir/PrintConfig.qll @@ -15,7 +15,7 @@ predicate locationIsInStandardHeaders(Location loc) { predicate shouldDumpFunction(Declaration decl) { not locationIsInStandardHeaders(decl.getLocation()) and ( - not decl instanceof Variable + decl instanceof Function or decl.(GlobalOrNamespaceVariable).hasInitializer() ) diff --git a/cpp/ql/test/library-tests/templates/CPP-203/decls.expected b/cpp/ql/test/library-tests/templates/CPP-203/decls.expected index b311041021d..33aa6114052 100644 --- a/cpp/ql/test/library-tests/templates/CPP-203/decls.expected +++ b/cpp/ql/test/library-tests/templates/CPP-203/decls.expected @@ -15,6 +15,7 @@ | test.cpp:3:8:3:8 | operator= | | test.cpp:3:8:3:10 | Str | | test.cpp:3:8:3:10 | Str | +| test.cpp:7:16:7:16 | T | | test.cpp:8:11:8:21 | val | | test.cpp:8:19:8:19 | val | | test.cpp:10:6:10:6 | f | diff --git a/cpp/ql/test/library-tests/variables/global/variables.expected b/cpp/ql/test/library-tests/variables/global/variables.expected index d66899e62fa..9d022a98264 100644 --- a/cpp/ql/test/library-tests/variables/global/variables.expected +++ b/cpp/ql/test/library-tests/variables/global/variables.expected @@ -4,11 +4,7 @@ | c.c:6:5:6:6 | ls | array of 4 {int} | 1 | | c.c:8:5:8:7 | iss | array of 4 {array of 2 {int}} | 1 | | c.c:12:11:12:11 | i | typedef {int} as "int_alias" | 1 | -| c.h:4:12:4:13 | ks | array of {int} | 1 | -| c.h:8:12:8:14 | iss | array of {array of 2 {int}} | 1 | -| c.h:10:12:10:12 | i | int | 1 | | d.cpp:3:7:3:8 | xs | array of {int} | 1 | -| d.h:3:14:3:15 | xs | array of 2 {int} | 1 | | file://:0:0:0:0 | (unnamed parameter 0) | reference to {const {struct __va_list_tag}} | 1 | | file://:0:0:0:0 | (unnamed parameter 0) | rvalue reference to {struct __va_list_tag} | 1 | | file://:0:0:0:0 | fp_offset | unsigned int | 1 | diff --git a/cpp/ql/test/library-tests/vector_types/builtin_ops.expected b/cpp/ql/test/library-tests/vector_types/builtin_ops.expected index 2c0dd9d0017..756ca2f1c17 100644 --- a/cpp/ql/test/library-tests/vector_types/builtin_ops.expected +++ b/cpp/ql/test/library-tests/vector_types/builtin_ops.expected @@ -1,2 +1,4 @@ +| vector_types2.cpp:10:15:10:42 | __builtin_shuffle | +| vector_types2.cpp:11:15:11:45 | __builtin_shuffle | | vector_types.cpp:31:13:31:49 | __builtin_shufflevector | | vector_types.cpp:58:10:58:52 | __builtin_convertvector | diff --git a/cpp/ql/test/library-tests/vector_types/variables.expected b/cpp/ql/test/library-tests/vector_types/variables.expected index 2494f192e9a..52fa98dd3f0 100644 --- a/cpp/ql/test/library-tests/vector_types/variables.expected +++ b/cpp/ql/test/library-tests/vector_types/variables.expected @@ -13,6 +13,12 @@ | file://:0:0:0:0 | gp_offset | gp_offset | file://:0:0:0:0 | unsigned int | 4 | | file://:0:0:0:0 | overflow_arg_area | overflow_arg_area | file://:0:0:0:0 | void * | 8 | | file://:0:0:0:0 | reg_save_area | reg_save_area | file://:0:0:0:0 | void * | 8 | +| vector_types2.cpp:5:7:5:7 | a | a | vector_types2.cpp:2:13:2:15 | v4i | 16 | +| vector_types2.cpp:6:7:6:7 | b | b | vector_types2.cpp:2:13:2:15 | v4i | 16 | +| vector_types2.cpp:7:7:7:12 | mask_1 | mask_1 | vector_types2.cpp:2:13:2:15 | v4i | 16 | +| vector_types2.cpp:8:7:8:12 | mask_2 | mask_2 | vector_types2.cpp:2:13:2:15 | v4i | 16 | +| vector_types2.cpp:10:7:10:11 | res_1 | res_1 | vector_types2.cpp:2:13:2:15 | v4i | 16 | +| vector_types2.cpp:11:7:11:11 | res_2 | res_2 | vector_types2.cpp:2:13:2:15 | v4i | 16 | | vector_types.cpp:9:21:9:21 | x | x | vector_types.cpp:6:15:6:17 | v4f | 16 | | vector_types.cpp:14:18:14:20 | lhs | lhs | vector_types.cpp:6:15:6:17 | v4f | 16 | | vector_types.cpp:14:27:14:29 | rhs | rhs | vector_types.cpp:6:15:6:17 | v4f | 16 | diff --git a/cpp/ql/test/library-tests/vector_types/vector_types2.cpp b/cpp/ql/test/library-tests/vector_types/vector_types2.cpp new file mode 100644 index 00000000000..d4233b28890 --- /dev/null +++ b/cpp/ql/test/library-tests/vector_types/vector_types2.cpp @@ -0,0 +1,12 @@ +// semmle-extractor-options: --gnu --gnu_version 80000 +typedef int v4i __attribute__((vector_size (16))); + +void f() { + v4i a = {1,2,3,4}; + v4i b = {5,6,7,8}; + v4i mask_1 = {3,0,1,2}; + v4i mask_2 = {3,5,4,2}; + + v4i res_1 = __builtin_shuffle(a, mask_1); + v4i res_2 = __builtin_shuffle(a, b, mask_2); +} diff --git a/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.expected b/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.expected similarity index 100% rename from cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.expected rename to cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.expected diff --git a/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.qlref b/cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.qlref similarity index 100% rename from cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/ImplicitDowncastFromBitfield.qlref rename to cpp/ql/test/query-tests/Likely Bugs/Conversion/LossyFunctionResultCast/LossyFunctionResultCast.qlref diff --git a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected index 6b8a59793a3..8f9d91fc1ad 100644 --- a/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected +++ b/cpp/ql/test/query-tests/Likely Bugs/Memory Management/ReturnStackAllocatedMemory/ReturnStackAllocatedMemory.expected @@ -100,12 +100,6 @@ edges | test.cpp:190:10:190:13 | Unary | test.cpp:190:10:190:13 | (reference dereference) | | test.cpp:190:10:190:13 | Unary | test.cpp:190:10:190:13 | (reference to) | | test.cpp:190:10:190:13 | pRef | test.cpp:190:10:190:13 | Unary | -| test.cpp:225:14:225:15 | px | test.cpp:226:10:226:11 | Load | -| test.cpp:226:10:226:11 | Load | test.cpp:226:10:226:11 | px | -| test.cpp:226:10:226:11 | px | test.cpp:226:10:226:11 | StoreValue | -| test.cpp:231:16:231:17 | & ... | test.cpp:225:14:225:15 | px | -| test.cpp:231:17:231:17 | Unary | test.cpp:231:16:231:17 | & ... | -| test.cpp:231:17:231:17 | x | test.cpp:231:17:231:17 | Unary | nodes | test.cpp:17:9:17:11 | & ... | semmle.label | & ... | | test.cpp:17:9:17:11 | StoreValue | semmle.label | StoreValue | @@ -221,13 +215,6 @@ nodes | test.cpp:190:10:190:13 | Unary | semmle.label | Unary | | test.cpp:190:10:190:13 | Unary | semmle.label | Unary | | test.cpp:190:10:190:13 | pRef | semmle.label | pRef | -| test.cpp:225:14:225:15 | px | semmle.label | px | -| test.cpp:226:10:226:11 | Load | semmle.label | Load | -| test.cpp:226:10:226:11 | StoreValue | semmle.label | StoreValue | -| test.cpp:226:10:226:11 | px | semmle.label | px | -| test.cpp:231:16:231:17 | & ... | semmle.label | & ... | -| test.cpp:231:17:231:17 | Unary | semmle.label | Unary | -| test.cpp:231:17:231:17 | x | semmle.label | x | #select | test.cpp:17:9:17:11 | StoreValue | test.cpp:17:10:17:11 | mc | test.cpp:17:9:17:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:17:10:17:11 | mc | mc | | test.cpp:25:9:25:11 | StoreValue | test.cpp:23:18:23:19 | mc | test.cpp:25:9:25:11 | StoreValue | May return stack-allocated memory from $@. | test.cpp:23:18:23:19 | mc | mc | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected index 7cefb7cfafc..885e188be64 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-134/semmle/globalVars/UncontrolledFormatString.expected @@ -1,4 +1,134 @@ edges +| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:27:9:27:12 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:33:15:33:18 | copy | +| globalVars.c:8:7:8:10 | copy | globalVars.c:35:11:35:14 | copy | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:38:9:38:13 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:44:15:44:19 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:9:7:9:11 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:11:22:11:25 | *argv | globalVars.c:12:2:12:15 | Store | +| globalVars.c:11:22:11:25 | argv | globalVars.c:12:2:12:15 | Store | +| globalVars.c:12:2:12:15 | Store | globalVars.c:8:7:8:10 | copy | +| globalVars.c:15:21:15:23 | val | globalVars.c:16:2:16:12 | Store | +| globalVars.c:16:2:16:12 | Store | globalVars.c:9:7:9:11 | copy2 | +| globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection | +| globalVars.c:24:11:24:14 | argv | globalVars.c:11:22:11:25 | argv | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv indirection | +| globalVars.c:24:11:24:14 | argv | globalVars.c:24:11:24:14 | argv indirection | +| globalVars.c:24:11:24:14 | argv indirection | globalVars.c:11:22:11:25 | *argv | +| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | (const char *)... | +| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy | +| globalVars.c:27:9:27:12 | copy | globalVars.c:27:9:27:12 | copy indirection | +| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy | +| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection | +| globalVars.c:30:15:30:18 | copy | globalVars.c:30:15:30:18 | copy indirection | +| globalVars.c:30:15:30:18 | copy | globalVars.c:35:11:35:14 | copy | +| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:19:25:19:27 | *str | +| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:30:15:30:18 | printWrapper output argument | +| globalVars.c:30:15:30:18 | printWrapper output argument | globalVars.c:35:11:35:14 | copy | +| globalVars.c:33:15:33:18 | copy | globalVars.c:35:11:35:14 | copy | +| globalVars.c:35:11:35:14 | copy | globalVars.c:15:21:15:23 | val | +| globalVars.c:35:11:35:14 | copy | globalVars.c:35:11:35:14 | copy | +| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | (const char *)... | +| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 | +| globalVars.c:38:9:38:13 | copy2 | globalVars.c:38:9:38:13 | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:41:15:41:19 | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | (const char *)... | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:19:25:19:27 | *str | +| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:41:15:41:19 | printWrapper output argument | +| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | (const char *)... | +| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:41:15:41:19 | printWrapper output argument | globalVars.c:50:9:50:13 | copy2 indirection | +| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | (const char *)... | +| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:44:15:44:19 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection | +| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | (const char *)... | +| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 | +| globalVars.c:50:9:50:13 | copy2 | globalVars.c:50:9:50:13 | copy2 indirection | subpaths +| globalVars.c:30:15:30:18 | copy indirection | globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection | globalVars.c:30:15:30:18 | printWrapper output argument | +| globalVars.c:41:15:41:19 | copy2 indirection | globalVars.c:19:25:19:27 | *str | globalVars.c:19:25:19:27 | ReturnIndirection | globalVars.c:41:15:41:19 | printWrapper output argument | nodes +| globalVars.c:8:7:8:10 | copy | semmle.label | copy | +| globalVars.c:9:7:9:11 | copy2 | semmle.label | copy2 | +| globalVars.c:11:22:11:25 | *argv | semmle.label | *argv | +| globalVars.c:11:22:11:25 | argv | semmle.label | argv | +| globalVars.c:12:2:12:15 | Store | semmle.label | Store | +| globalVars.c:15:21:15:23 | val | semmle.label | val | +| globalVars.c:16:2:16:12 | Store | semmle.label | Store | +| globalVars.c:19:25:19:27 | *str | semmle.label | *str | +| globalVars.c:19:25:19:27 | ReturnIndirection | semmle.label | ReturnIndirection | +| globalVars.c:24:11:24:14 | argv | semmle.label | argv | +| globalVars.c:24:11:24:14 | argv | semmle.label | argv | +| globalVars.c:24:11:24:14 | argv | semmle.label | argv | +| globalVars.c:24:11:24:14 | argv indirection | semmle.label | argv indirection | +| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:27:9:27:12 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:27:9:27:12 | copy | semmle.label | copy | +| globalVars.c:27:9:27:12 | copy | semmle.label | copy | +| globalVars.c:27:9:27:12 | copy | semmle.label | copy | +| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection | +| globalVars.c:27:9:27:12 | copy indirection | semmle.label | copy indirection | +| globalVars.c:30:15:30:18 | copy | semmle.label | copy | +| globalVars.c:30:15:30:18 | copy | semmle.label | copy | +| globalVars.c:30:15:30:18 | copy | semmle.label | copy | +| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection | +| globalVars.c:30:15:30:18 | copy indirection | semmle.label | copy indirection | +| globalVars.c:30:15:30:18 | printWrapper output argument | semmle.label | printWrapper output argument | +| globalVars.c:33:15:33:18 | copy | semmle.label | copy | +| globalVars.c:35:11:35:14 | copy | semmle.label | copy | +| globalVars.c:35:11:35:14 | copy | semmle.label | copy | +| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:38:9:38:13 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 | +| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 | +| globalVars.c:38:9:38:13 | copy2 | semmle.label | copy2 | +| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection | +| globalVars.c:38:9:38:13 | copy2 indirection | semmle.label | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 | +| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 | +| globalVars.c:41:15:41:19 | copy2 | semmle.label | copy2 | +| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection | +| globalVars.c:41:15:41:19 | copy2 indirection | semmle.label | copy2 indirection | +| globalVars.c:41:15:41:19 | printWrapper output argument | semmle.label | printWrapper output argument | +| globalVars.c:44:15:44:19 | copy2 | semmle.label | copy2 | +| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:50:9:50:13 | (const char *)... | semmle.label | (const char *)... | +| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 | +| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 | +| globalVars.c:50:9:50:13 | copy2 | semmle.label | copy2 | +| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection | +| globalVars.c:50:9:50:13 | copy2 indirection | semmle.label | copy2 indirection | #select +| globalVars.c:27:9:27:12 | copy | globalVars.c:24:11:24:14 | argv | globalVars.c:27:9:27:12 | copy | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | globalVars.c:24:11:24:14 | argv | argv | +| globalVars.c:30:15:30:18 | copy | globalVars.c:24:11:24:14 | argv | globalVars.c:30:15:30:18 | copy | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(str), which calls printf(format) | globalVars.c:24:11:24:14 | argv | argv | +| globalVars.c:38:9:38:13 | copy2 | globalVars.c:24:11:24:14 | argv | globalVars.c:38:9:38:13 | copy2 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | globalVars.c:24:11:24:14 | argv | argv | +| globalVars.c:41:15:41:19 | copy2 | globalVars.c:24:11:24:14 | argv | globalVars.c:41:15:41:19 | copy2 | The value of this argument may come from $@ and is being used as a formatting argument to printWrapper(str), which calls printf(format) | globalVars.c:24:11:24:14 | argv | argv | +| globalVars.c:50:9:50:13 | copy2 | globalVars.c:24:11:24:14 | argv | globalVars.c:50:9:50:13 | copy2 | The value of this argument may come from $@ and is being used as a formatting argument to printf(format) | globalVars.c:24:11:24:14 | argv | argv | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected index 80b195bd0bd..11ec8e849a5 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/ExposedSystemData.expected @@ -1,4 +1,8 @@ edges +| tests2.cpp:50:13:50:19 | global1 | tests2.cpp:82:14:82:20 | global1 | +| tests2.cpp:50:13:50:19 | global1 | tests2.cpp:82:14:82:20 | global1 | +| tests2.cpp:50:23:50:43 | Store | tests2.cpp:50:13:50:19 | global1 | +| tests2.cpp:50:23:50:43 | call to mysql_get_client_info | tests2.cpp:50:23:50:43 | Store | | tests2.cpp:63:13:63:18 | call to getenv | tests2.cpp:63:13:63:26 | (const char *)... | | tests2.cpp:64:13:64:18 | call to getenv | tests2.cpp:64:13:64:26 | (const char *)... | | tests2.cpp:65:13:65:18 | call to getenv | tests2.cpp:65:13:65:30 | (const char *)... | @@ -6,6 +10,8 @@ edges | tests2.cpp:78:18:78:38 | call to mysql_get_client_info | tests2.cpp:81:14:81:19 | (const char *)... | | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | +| tests2.cpp:82:14:82:20 | global1 | tests2.cpp:82:14:82:20 | global1 | +| tests2.cpp:82:14:82:20 | global1 | tests2.cpp:82:14:82:20 | global1 | | tests2.cpp:91:42:91:45 | str1 | tests2.cpp:93:14:93:17 | str1 | | tests2.cpp:101:8:101:15 | call to getpwuid | tests2.cpp:102:14:102:15 | pw | | tests2.cpp:109:3:109:4 | c1 [post update] [ptr] | tests2.cpp:111:14:111:15 | c1 [read] [ptr] | @@ -23,6 +29,9 @@ edges | tests_sysconf.cpp:36:21:36:27 | confstr output argument | tests_sysconf.cpp:39:19:39:25 | (const void *)... | | tests_sysconf.cpp:36:21:36:27 | confstr output argument | tests_sysconf.cpp:39:19:39:25 | pathbuf | nodes +| tests2.cpp:50:13:50:19 | global1 | semmle.label | global1 | +| tests2.cpp:50:23:50:43 | Store | semmle.label | Store | +| tests2.cpp:50:23:50:43 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info | | tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv | | tests2.cpp:63:13:63:18 | call to getenv | semmle.label | call to getenv | | tests2.cpp:63:13:63:26 | (const char *)... | semmle.label | (const char *)... | @@ -39,6 +48,8 @@ nodes | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info | | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | semmle.label | call to mysql_get_client_info | | tests2.cpp:81:14:81:19 | (const char *)... | semmle.label | (const char *)... | +| tests2.cpp:82:14:82:20 | global1 | semmle.label | global1 | +| tests2.cpp:82:14:82:20 | global1 | semmle.label | global1 | | tests2.cpp:91:42:91:45 | str1 | semmle.label | str1 | | tests2.cpp:93:14:93:17 | str1 | semmle.label | str1 | | tests2.cpp:101:8:101:15 | call to getpwuid | semmle.label | call to getpwuid | @@ -70,6 +81,7 @@ subpaths | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | call to mysql_get_client_info | | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | This operation exposes system data from $@. | tests2.cpp:80:14:80:34 | call to mysql_get_client_info | call to mysql_get_client_info | | tests2.cpp:81:14:81:19 | (const char *)... | tests2.cpp:78:18:78:38 | call to mysql_get_client_info | tests2.cpp:81:14:81:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:78:18:78:38 | call to mysql_get_client_info | call to mysql_get_client_info | +| tests2.cpp:82:14:82:20 | global1 | tests2.cpp:50:23:50:43 | call to mysql_get_client_info | tests2.cpp:82:14:82:20 | global1 | This operation exposes system data from $@. | tests2.cpp:50:23:50:43 | call to mysql_get_client_info | call to mysql_get_client_info | | tests2.cpp:93:14:93:17 | str1 | tests2.cpp:91:42:91:45 | str1 | tests2.cpp:93:14:93:17 | str1 | This operation exposes system data from $@. | tests2.cpp:91:42:91:45 | str1 | str1 | | tests2.cpp:102:14:102:15 | pw | tests2.cpp:101:8:101:15 | call to getpwuid | tests2.cpp:102:14:102:15 | pw | This operation exposes system data from $@. | tests2.cpp:101:8:101:15 | call to getpwuid | call to getpwuid | | tests2.cpp:111:14:111:19 | (const char *)... | tests2.cpp:109:12:109:17 | call to getenv | tests2.cpp:111:14:111:19 | (const char *)... | This operation exposes system data from $@. | tests2.cpp:109:12:109:17 | call to getenv | call to getenv | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected index 62fe44dcc23..ff10a3b9c1c 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/PotentiallyExposedSystemData.expected @@ -5,6 +5,14 @@ edges | tests.cpp:57:18:57:23 | call to getenv | tests.cpp:57:18:57:39 | (const char_type *)... | | tests.cpp:58:41:58:46 | call to getenv | tests.cpp:58:41:58:62 | (const char_type *)... | | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:64 | (const char *)... | +| tests.cpp:62:7:62:18 | global_token | tests.cpp:69:17:69:28 | global_token | +| tests.cpp:62:7:62:18 | global_token | tests.cpp:71:27:71:38 | global_token | +| tests.cpp:62:7:62:18 | global_token | tests.cpp:71:27:71:38 | global_token | +| tests.cpp:62:22:62:27 | Store | tests.cpp:62:7:62:18 | global_token | +| tests.cpp:62:22:62:27 | call to getenv | tests.cpp:62:22:62:27 | Store | +| tests.cpp:69:17:69:28 | global_token | tests.cpp:73:27:73:31 | maybe | +| tests.cpp:71:27:71:38 | global_token | tests.cpp:71:27:71:38 | global_token | +| tests.cpp:71:27:71:38 | global_token | tests.cpp:71:27:71:38 | global_token | | tests.cpp:86:29:86:31 | *msg | tests.cpp:88:15:88:17 | msg | | tests.cpp:86:29:86:31 | msg | tests.cpp:88:15:88:17 | msg | | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:34 | (const char *)... | @@ -52,6 +60,13 @@ nodes | tests.cpp:59:43:59:48 | call to getenv | semmle.label | call to getenv | | tests.cpp:59:43:59:48 | call to getenv | semmle.label | call to getenv | | tests.cpp:59:43:59:64 | (const char *)... | semmle.label | (const char *)... | +| tests.cpp:62:7:62:18 | global_token | semmle.label | global_token | +| tests.cpp:62:22:62:27 | Store | semmle.label | Store | +| tests.cpp:62:22:62:27 | call to getenv | semmle.label | call to getenv | +| tests.cpp:69:17:69:28 | global_token | semmle.label | global_token | +| tests.cpp:71:27:71:38 | global_token | semmle.label | global_token | +| tests.cpp:71:27:71:38 | global_token | semmle.label | global_token | +| tests.cpp:73:27:73:31 | maybe | semmle.label | maybe | | tests.cpp:86:29:86:31 | *msg | semmle.label | *msg | | tests.cpp:86:29:86:31 | msg | semmle.label | msg | | tests.cpp:88:15:88:17 | msg | semmle.label | msg | @@ -97,6 +112,8 @@ subpaths | tests.cpp:58:41:58:62 | (const char_type *)... | tests.cpp:58:41:58:46 | call to getenv | tests.cpp:58:41:58:62 | (const char_type *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:58:41:58:46 | call to getenv | call to getenv | | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:48 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:59:43:59:48 | call to getenv | call to getenv | | tests.cpp:59:43:59:64 | (const char *)... | tests.cpp:59:43:59:48 | call to getenv | tests.cpp:59:43:59:64 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:59:43:59:48 | call to getenv | call to getenv | +| tests.cpp:71:27:71:38 | global_token | tests.cpp:62:22:62:27 | call to getenv | tests.cpp:71:27:71:38 | global_token | This operation potentially exposes sensitive system data from $@. | tests.cpp:62:22:62:27 | call to getenv | call to getenv | +| tests.cpp:73:27:73:31 | maybe | tests.cpp:62:22:62:27 | call to getenv | tests.cpp:73:27:73:31 | maybe | This operation potentially exposes sensitive system data from $@. | tests.cpp:62:22:62:27 | call to getenv | call to getenv | | tests.cpp:88:15:88:17 | msg | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:88:15:88:17 | msg | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv | | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:18 | call to getenv | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv | | tests.cpp:97:13:97:34 | (const char *)... | tests.cpp:97:13:97:18 | call to getenv | tests.cpp:97:13:97:34 | (const char *)... | This operation potentially exposes sensitive system data from $@. | tests.cpp:97:13:97:18 | call to getenv | call to getenv | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp index e61fc582fc3..843d579386b 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests.cpp @@ -68,9 +68,9 @@ void test2(bool cond) maybe = cond ? global_token : global_other; - printf("token = '%s'\n", global_token); // BAD: outputs SECRET_TOKEN environment variable [NOT DETECTED] + printf("token = '%s'\n", global_token); // BAD: outputs SECRET_TOKEN environment variable printf("other = '%s'\n", global_other); - printf("maybe = '%s'\n", maybe); // BAD: may output SECRET_TOKEN environment variable [NOT DETECTED] + printf("maybe = '%s'\n", maybe); // BAD: may output SECRET_TOKEN environment variable } void test3() diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp index 763f1ecfafc..e1399eab343 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-497/semmle/tests/tests2.cpp @@ -79,7 +79,7 @@ void test1() send(sock, mysql_get_client_info(), val(), val()); // BAD send(sock, buffer, val(), val()); // BAD - send(sock, global1, val(), val()); // BAD [NOT DETECTED] + send(sock, global1, val(), val()); // BAD send(sock, global2, val(), val()); // GOOD: not system data } diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected index a4db6155e31..7403cba893a 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected @@ -2,11 +2,26 @@ edges | tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p | | tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p | | tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p | +| tests3.cpp:35:16:35:20 | p_3_3 | tests3.cpp:38:2:38:6 | p_3_3 | +| tests3.cpp:35:24:35:56 | Store | tests3.cpp:35:16:35:20 | p_3_3 | +| tests3.cpp:35:24:35:56 | call to createXMLReader | tests3.cpp:35:24:35:56 | Store | +| tests3.cpp:41:16:41:20 | p_3_4 | tests3.cpp:45:2:45:6 | p_3_4 | +| tests3.cpp:41:24:41:56 | Store | tests3.cpp:41:16:41:20 | p_3_4 | +| tests3.cpp:41:24:41:56 | call to createXMLReader | tests3.cpp:41:24:41:56 | Store | +| tests3.cpp:48:16:48:20 | p_3_5 | tests3.cpp:56:2:56:6 | p_3_5 | +| tests3.cpp:48:24:48:56 | Store | tests3.cpp:48:16:48:20 | p_3_5 | +| tests3.cpp:48:24:48:56 | call to createXMLReader | tests3.cpp:48:24:48:56 | Store | | tests3.cpp:60:21:60:53 | call to createXMLReader | tests3.cpp:63:2:63:2 | p | | tests3.cpp:67:21:67:53 | call to createXMLReader | tests3.cpp:70:2:70:2 | p | | tests5.cpp:27:25:27:38 | call to createLSParser | tests5.cpp:29:2:29:2 | p | | tests5.cpp:40:25:40:38 | call to createLSParser | tests5.cpp:43:2:43:2 | p | | tests5.cpp:55:25:55:38 | call to createLSParser | tests5.cpp:59:2:59:2 | p | +| tests5.cpp:63:14:63:17 | g_p1 | tests5.cpp:76:2:76:5 | g_p1 | +| tests5.cpp:63:21:63:24 | g_p2 | tests5.cpp:77:2:77:5 | g_p2 | +| tests5.cpp:67:2:67:32 | Store | tests5.cpp:63:14:63:17 | g_p1 | +| tests5.cpp:67:17:67:30 | call to createLSParser | tests5.cpp:67:2:67:32 | Store | +| tests5.cpp:70:2:70:32 | Store | tests5.cpp:63:21:63:24 | g_p2 | +| tests5.cpp:70:17:70:30 | call to createLSParser | tests5.cpp:70:2:70:32 | Store | | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p | | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p | | tests5.cpp:83:2:83:2 | p | tests5.cpp:85:2:85:2 | p | @@ -46,6 +61,18 @@ nodes | tests2.cpp:37:2:37:2 | p | semmle.label | p | | tests3.cpp:23:21:23:53 | call to createXMLReader | semmle.label | call to createXMLReader | | tests3.cpp:25:2:25:2 | p | semmle.label | p | +| tests3.cpp:35:16:35:20 | p_3_3 | semmle.label | p_3_3 | +| tests3.cpp:35:24:35:56 | Store | semmle.label | Store | +| tests3.cpp:35:24:35:56 | call to createXMLReader | semmle.label | call to createXMLReader | +| tests3.cpp:38:2:38:6 | p_3_3 | semmle.label | p_3_3 | +| tests3.cpp:41:16:41:20 | p_3_4 | semmle.label | p_3_4 | +| tests3.cpp:41:24:41:56 | Store | semmle.label | Store | +| tests3.cpp:41:24:41:56 | call to createXMLReader | semmle.label | call to createXMLReader | +| tests3.cpp:45:2:45:6 | p_3_4 | semmle.label | p_3_4 | +| tests3.cpp:48:16:48:20 | p_3_5 | semmle.label | p_3_5 | +| tests3.cpp:48:24:48:56 | Store | semmle.label | Store | +| tests3.cpp:48:24:48:56 | call to createXMLReader | semmle.label | call to createXMLReader | +| tests3.cpp:56:2:56:6 | p_3_5 | semmle.label | p_3_5 | | tests3.cpp:60:21:60:53 | call to createXMLReader | semmle.label | call to createXMLReader | | tests3.cpp:63:2:63:2 | p | semmle.label | p | | tests3.cpp:67:21:67:53 | call to createXMLReader | semmle.label | call to createXMLReader | @@ -61,6 +88,14 @@ nodes | tests5.cpp:43:2:43:2 | p | semmle.label | p | | tests5.cpp:55:25:55:38 | call to createLSParser | semmle.label | call to createLSParser | | tests5.cpp:59:2:59:2 | p | semmle.label | p | +| tests5.cpp:63:14:63:17 | g_p1 | semmle.label | g_p1 | +| tests5.cpp:63:21:63:24 | g_p2 | semmle.label | g_p2 | +| tests5.cpp:67:2:67:32 | Store | semmle.label | Store | +| tests5.cpp:67:17:67:30 | call to createLSParser | semmle.label | call to createLSParser | +| tests5.cpp:70:2:70:32 | Store | semmle.label | Store | +| tests5.cpp:70:17:70:30 | call to createLSParser | semmle.label | call to createLSParser | +| tests5.cpp:76:2:76:5 | g_p1 | semmle.label | g_p1 | +| tests5.cpp:77:2:77:5 | g_p2 | semmle.label | g_p2 | | tests5.cpp:81:25:81:38 | call to createLSParser | semmle.label | call to createLSParser | | tests5.cpp:83:2:83:2 | p | semmle.label | p | | tests5.cpp:83:2:83:2 | p | semmle.label | p | @@ -108,6 +143,9 @@ subpaths | tests2.cpp:22:2:22:2 | p | tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:20:17:20:31 | SAXParser output argument | XML parser | | tests2.cpp:37:2:37:2 | p | tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:33:17:33:31 | SAXParser output argument | XML parser | | tests3.cpp:25:2:25:2 | p | tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:23:21:23:53 | call to createXMLReader | XML parser | +| tests3.cpp:38:2:38:6 | p_3_3 | tests3.cpp:35:24:35:56 | call to createXMLReader | tests3.cpp:38:2:38:6 | p_3_3 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:35:24:35:56 | call to createXMLReader | XML parser | +| tests3.cpp:45:2:45:6 | p_3_4 | tests3.cpp:41:24:41:56 | call to createXMLReader | tests3.cpp:45:2:45:6 | p_3_4 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:41:24:41:56 | call to createXMLReader | XML parser | +| tests3.cpp:56:2:56:6 | p_3_5 | tests3.cpp:48:24:48:56 | call to createXMLReader | tests3.cpp:56:2:56:6 | p_3_5 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:48:24:48:56 | call to createXMLReader | XML parser | | tests3.cpp:63:2:63:2 | p | tests3.cpp:60:21:60:53 | call to createXMLReader | tests3.cpp:63:2:63:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:60:21:60:53 | call to createXMLReader | XML parser | | tests3.cpp:70:2:70:2 | p | tests3.cpp:67:21:67:53 | call to createXMLReader | tests3.cpp:70:2:70:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:67:21:67:53 | call to createXMLReader | XML parser | | tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:26:34:26:48 | (int)... | XML parser | @@ -118,6 +156,8 @@ subpaths | tests5.cpp:29:2:29:2 | p | tests5.cpp:27:25:27:38 | call to createLSParser | tests5.cpp:29:2:29:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:27:25:27:38 | call to createLSParser | XML parser | | tests5.cpp:43:2:43:2 | p | tests5.cpp:40:25:40:38 | call to createLSParser | tests5.cpp:43:2:43:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:40:25:40:38 | call to createLSParser | XML parser | | tests5.cpp:59:2:59:2 | p | tests5.cpp:55:25:55:38 | call to createLSParser | tests5.cpp:59:2:59:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:55:25:55:38 | call to createLSParser | XML parser | +| tests5.cpp:76:2:76:5 | g_p1 | tests5.cpp:67:17:67:30 | call to createLSParser | tests5.cpp:76:2:76:5 | g_p1 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:67:17:67:30 | call to createLSParser | XML parser | +| tests5.cpp:77:2:77:5 | g_p2 | tests5.cpp:70:17:70:30 | call to createLSParser | tests5.cpp:77:2:77:5 | g_p2 | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:70:17:70:30 | call to createLSParser | XML parser | | tests5.cpp:83:2:83:2 | p | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:83:2:83:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:81:25:81:38 | call to createLSParser | XML parser | | tests5.cpp:89:2:89:2 | p | tests5.cpp:81:25:81:38 | call to createLSParser | tests5.cpp:89:2:89:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests5.cpp:81:25:81:38 | call to createLSParser | XML parser | | tests.cpp:17:2:17:2 | p | tests.cpp:15:23:15:43 | XercesDOMParser output argument | tests.cpp:17:2:17:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests.cpp:15:23:15:43 | XercesDOMParser output argument | XML parser | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp index 15e518daf13..8af560a8e6d 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp @@ -35,14 +35,14 @@ void test3_2(InputSource &data) { SAX2XMLReader *p_3_3 = XMLReaderFactory::createXMLReader(); void test3_3(InputSource &data) { - p_3_3->parse(data); // BAD (parser not correctly configured) [NOT DETECTED] + p_3_3->parse(data); // BAD (parser not correctly configured) } SAX2XMLReader *p_3_4 = XMLReaderFactory::createXMLReader(); void test3_4(InputSource &data) { p_3_4->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true); - p_3_4->parse(data); // GOOD + p_3_4->parse(data); // GOOD [FALSE POSITIVE] } SAX2XMLReader *p_3_5 = XMLReaderFactory::createXMLReader(); @@ -53,7 +53,7 @@ void test3_5_init() { void test3_5(InputSource &data) { test3_5_init(); - p_3_5->parse(data); // GOOD + p_3_5->parse(data); // GOOD [FALSE POSITIVE] } void test3_6(InputSource &data) { diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp index 99027c9bd93..0d55be455da 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-611/tests5.cpp @@ -73,8 +73,8 @@ void test5_6_init() { void test5_6() { test5_6_init(); - g_p1->parse(*g_data); // GOOD - g_p2->parse(*g_data); // BAD (parser not correctly configured) [NOT DETECTED] + g_p1->parse(*g_data); // GOOD [FALSE POSITIVE] + g_p2->parse(*g_data); // BAD (parser not correctly configured) } void test5_7(DOMImplementationLS *impl, InputSource &data) { diff --git a/csharp/codeql-extractor.yml b/csharp/codeql-extractor.yml index 5d498049a8a..4e1fa7934ce 100644 --- a/csharp/codeql-extractor.yml +++ b/csharp/codeql-extractor.yml @@ -4,12 +4,6 @@ version: 1.22.1 column_kind: "utf16" extra_env_vars: DOTNET_GENERATE_ASPNET_CERTIFICATE: "false" - COR_ENABLE_PROFILING: "1" - COR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}" - COR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}" - CORECLR_ENABLE_PROFILING: "1" - CORECLR_PROFILER: "{A3C70A64-7D41-4A94-A3F6-FD47D9259997}" - CORECLR_PROFILER_PATH_64: "${env.CODEQL_EXTRACTOR_CSHARP_ROOT}/tools/${env.CODEQL_PLATFORM}/clrtracer64${env.CODEQL_PLATFORM_DLL_EXTENSION}" file_types: - name: cs display_name: C# sources diff --git a/csharp/documentation/library-coverage/coverage.csv b/csharp/documentation/library-coverage/coverage.csv index 2bc6ca863ac..485242620f3 100644 --- a/csharp/documentation/library-coverage/coverage.csv +++ b/csharp/documentation/library-coverage/coverage.csv @@ -1,27 +1,28 @@ -package,sink,source,summary,sink:code,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value -Dapper,55,,,,,,55,,,, -JsonToItemsTaskFactory,,,7,,,,,,,7, -Microsoft.ApplicationBlocks.Data,28,,,,,,28,,,, -Microsoft.CSharp,,,24,,,,,,,24, -Microsoft.EntityFrameworkCore,6,,,,,,6,,,, -Microsoft.Extensions.Caching.Distributed,,,15,,,,,,,15, -Microsoft.Extensions.Caching.Memory,,,46,,,,,,,45,1 -Microsoft.Extensions.Configuration,,,83,,,,,,,80,3 -Microsoft.Extensions.DependencyInjection,,,62,,,,,,,62, -Microsoft.Extensions.DependencyModel,,,12,,,,,,,12, -Microsoft.Extensions.FileProviders,,,15,,,,,,,15, -Microsoft.Extensions.FileSystemGlobbing,,,15,,,,,,,13,2 -Microsoft.Extensions.Hosting,,,17,,,,,,,16,1 -Microsoft.Extensions.Http,,,10,,,,,,,10, -Microsoft.Extensions.Logging,,,37,,,,,,,37, -Microsoft.Extensions.Options,,,8,,,,,,,8, -Microsoft.Extensions.Primitives,,,63,,,,,,,63, -Microsoft.Interop,,,27,,,,,,,27, -Microsoft.NET.Build.Tasks,,,1,,,,,,,1, -Microsoft.NETCore.Platforms.BuildTasks,,,4,,,,,,,4, -Microsoft.VisualBasic,,,9,,,,,,,5,4 -Microsoft.Win32,,,8,,,,,,,8, -MySql.Data.MySqlClient,48,,,,,,48,,,, -Newtonsoft.Json,,,91,,,,,,,73,18 -ServiceStack,194,,7,27,,75,92,,,7, -System,28,3,12038,,4,,23,1,3,10096,1942 +package,sink,source,summary,sink:code,sink:encryption-decryptor,sink:encryption-encryptor,sink:encryption-keyprop,sink:encryption-symmetrickey,sink:html,sink:remote,sink:sql,sink:xss,source:local,summary:taint,summary:value +Dapper,55,,,,,,,,,,55,,,, +JsonToItemsTaskFactory,,,7,,,,,,,,,,,7, +Microsoft.ApplicationBlocks.Data,28,,,,,,,,,,28,,,, +Microsoft.CSharp,,,24,,,,,,,,,,,24, +Microsoft.EntityFrameworkCore,6,,,,,,,,,,6,,,, +Microsoft.Extensions.Caching.Distributed,,,15,,,,,,,,,,,15, +Microsoft.Extensions.Caching.Memory,,,46,,,,,,,,,,,45,1 +Microsoft.Extensions.Configuration,,,83,,,,,,,,,,,80,3 +Microsoft.Extensions.DependencyInjection,,,62,,,,,,,,,,,62, +Microsoft.Extensions.DependencyModel,,,12,,,,,,,,,,,12, +Microsoft.Extensions.FileProviders,,,15,,,,,,,,,,,15, +Microsoft.Extensions.FileSystemGlobbing,,,15,,,,,,,,,,,13,2 +Microsoft.Extensions.Hosting,,,17,,,,,,,,,,,16,1 +Microsoft.Extensions.Http,,,10,,,,,,,,,,,10, +Microsoft.Extensions.Logging,,,37,,,,,,,,,,,37, +Microsoft.Extensions.Options,,,8,,,,,,,,,,,8, +Microsoft.Extensions.Primitives,,,63,,,,,,,,,,,63, +Microsoft.Interop,,,27,,,,,,,,,,,27, +Microsoft.NET.Build.Tasks,,,1,,,,,,,,,,,1, +Microsoft.NETCore.Platforms.BuildTasks,,,4,,,,,,,,,,,4, +Microsoft.VisualBasic,,,9,,,,,,,,,,,5,4 +Microsoft.Win32,,,8,,,,,,,,,,,8, +MySql.Data.MySqlClient,48,,,,,,,,,,48,,,, +Newtonsoft.Json,,,91,,,,,,,,,,,73,18 +ServiceStack,194,,7,27,,,,,,75,92,,,7, +System,35,3,11796,,1,1,1,,4,,25,3,3,9854,1942 +Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,, diff --git a/csharp/documentation/library-coverage/coverage.rst b/csharp/documentation/library-coverage/coverage.rst index 076d2078d4b..d088c041e8b 100644 --- a/csharp/documentation/library-coverage/coverage.rst +++ b/csharp/documentation/library-coverage/coverage.rst @@ -8,7 +8,7 @@ C# framework & library support Framework / library,Package,Flow sources,Taint & value steps,Sinks (total),`CWE-079` :sub:`Cross-site scripting` `ServiceStack `_,"``ServiceStack.*``, ``ServiceStack``",,7,194, - System,"``System.*``, ``System``",3,12038,28,5 - Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``",,554,137, - Totals,,3,12599,359,5 + System,"``System.*``, ``System``",3,11796,35,7 + Others,"``Dapper``, ``JsonToItemsTaskFactory``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.CSharp``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.NETCore.Platforms.BuildTasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``MySql.Data.MySqlClient``, ``Newtonsoft.Json``, ``Windows.Security.Cryptography.Core``",,554,138, + Totals,,3,12357,367,7 diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs index 8d63c6288bf..ec4f44c21c7 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs @@ -99,12 +99,6 @@ namespace Semmle.Extraction.CSharp using var logger = MakeLogger(options.Verbosity, options.Console); - if (Environment.GetEnvironmentVariable("SEMMLE_CLRTRACER") == "1" && !options.ClrTracer) - { - logger.Log(Severity.Info, "Skipping extraction since already extracted from the CLR tracer"); - return ExitCode.Ok; - } - var canonicalPathCache = CanonicalPathCache.Create(logger, 1000); var pathTransformer = new PathTransformer(canonicalPathCache); diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs index 7b4e0e95ea3..c2d21d6a16a 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Options.cs @@ -27,11 +27,6 @@ namespace Semmle.Extraction.CSharp /// public IList CompilerArguments { get; } = new List(); - /// - /// Holds if the extractor was launched from the CLR tracer. - /// - public bool ClrTracer { get; private set; } = false; - /// /// Holds if assembly information should be prefixed to TRAP labels. /// @@ -87,9 +82,6 @@ namespace Semmle.Extraction.CSharp { switch (flag) { - case "clrtracer": - ClrTracer = value; - return true; case "assemblysensitivetrap": AssemblySensitiveTrap = value; return true; diff --git a/csharp/extractor/Semmle.Extraction.Tests/Options.cs b/csharp/extractor/Semmle.Extraction.Tests/Options.cs index 9b17320fbaa..2637eb3d5f4 100644 --- a/csharp/extractor/Semmle.Extraction.Tests/Options.cs +++ b/csharp/extractor/Semmle.Extraction.Tests/Options.cs @@ -29,7 +29,6 @@ namespace Semmle.Extraction.Tests Assert.True(options.Threads >= 1); Assert.Equal(Verbosity.Info, options.Verbosity); Assert.False(options.Console); - Assert.False(options.ClrTracer); Assert.False(options.PDB); Assert.False(options.Fast); Assert.Equal(TrapWriter.CompressionMode.Brotli, options.TrapCompression); diff --git a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md index 30c583ee913..0efa6239b0f 100644 --- a/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.2 + +## 1.2.1 + ## 1.2.0 ## 1.1.4 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 75430e73d1c..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.2.0 +lastReleaseVersion: 1.2.2 diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index bc7eaf4142c..78cc75ede63 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-all -version: 1.2.1-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 30c583ee913..0efa6239b0f 100644 --- a/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md +++ b/csharp/ql/campaigns/Solorigate/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.2.2 + +## 1.2.1 + ## 1.2.0 ## 1.1.4 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 75430e73d1c..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.2.0 +lastReleaseVersion: 1.2.2 diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index 00725e23666..fced50b6ef4 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-solorigate-queries -version: 1.2.1-dev +version: 1.2.3-dev groups: - csharp - solorigate diff --git a/csharp/ql/lib/CHANGELOG.md b/csharp/ql/lib/CHANGELOG.md index 3f49fe5ade3..5ea16d73e48 100644 --- a/csharp/ql/lib/CHANGELOG.md +++ b/csharp/ql/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.3.2 + +## 0.3.1 + ## 0.3.0 ### Deprecated APIs 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 95f6e3a0ba6..18c64250f42 100644 --- a/csharp/ql/lib/codeql-pack.release.yml +++ b/csharp/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.2 diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 3f371c01e92..8f932e28c7a 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-all -version: 0.3.1-dev +version: 0.3.3-dev groups: csharp dbscheme: semmlecode.csharp.dbscheme extractor: csharp diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll index d9bebb9bf17..b63d804216b 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll @@ -335,7 +335,7 @@ module Expressions { // ```csharp // new Dictionary() { [0] = "Zero", [1] = "One", [2] = "Two" } // ``` - // need special treatment, because the the accesses `[0]`, `[1]`, and `[2]` + // need special treatment, because the accesses `[0]`, `[1]`, and `[2]` // have no qualifier. this = any(MemberInitializer mi).getLValue() } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll index 47fcd24883c..7d0dd10c084 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll @@ -881,7 +881,12 @@ import Cached * graph is restricted to nodes from `RelevantNode`. */ module TestOutput { - abstract class RelevantNode extends Node { } + abstract class RelevantNode extends Node { + /** + * Gets a string used to resolve ties in node and edge ordering. + */ + string getOrderDisambuigation() { result = "" } + } query predicate nodes(RelevantNode n, string attr, string val) { attr = "semmle.order" and @@ -894,7 +899,8 @@ module TestOutput { p order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(), + p.getOrderDisambuigation() ) ).toString() } @@ -916,7 +922,8 @@ module TestOutput { s order by l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString() + l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(), + s.getOrderDisambuigation() ) ).toString() } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll index 782d33ea020..904ac79f009 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll @@ -377,6 +377,7 @@ module CsvValidation { exists(string row, string kind | sinkModel(row) | kind = row.splitAt(";", 7) and not kind = ["code", "sql", "xss", "remote", "html"] and + not kind.matches("encryption-%") and msg = "Invalid kind \"" + kind + "\" in sink model." ) or diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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 a076f7a2e45..340bfe280b7 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImplForContentDataFlow.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/frameworks/generated/dotnet/Runtime.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll index b3f247ede94..f4c5596c973 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/generated/dotnet/Runtime.qll @@ -1,11 +1,23 @@ /** * THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. - * Definitions of taint steps in the dotnetruntime framework. + * Definitions of taint steps in the Runtime framework. */ import csharp private import semmle.code.csharp.dataflow.ExternalFlow +private class RuntimeSinksCsv extends SinkModelCsv { + override predicate row(string row) { + row = + [ + "System.Data.Odbc;OdbcDataAdapter;false;OdbcDataAdapter;(System.String,System.Data.Odbc.OdbcConnection);;Argument[0];sql;generated", + "System.Data.Odbc;OdbcDataAdapter;false;OdbcDataAdapter;(System.String,System.String);;Argument[0];sql;generated", + "System.Net.Http;StringContent;false;StringContent;(System.String);;Argument[0];xss;generated", + "System.Net.Http;StringContent;false;StringContent;(System.String,System.Text.Encoding);;Argument[0];xss;generated" + ] + } +} + private class RuntimeSummaryCsv extends SummaryModelCsv { override predicate row(string row) { row = @@ -529,11 +541,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.CodeDom.Compiler;GeneratedCodeAttribute;false;GeneratedCodeAttribute;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated", "System.CodeDom.Compiler;GeneratedCodeAttribute;false;get_Tool;();;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom.Compiler;GeneratedCodeAttribute;false;get_Version;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.CodeDom.Compiler;IndentedTextWriter;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom.Compiler;IndentedTextWriter;false;IndentedTextWriter;(System.IO.TextWriter,System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.CodeDom.Compiler;IndentedTextWriter;false;IndentedTextWriter;(System.IO.TextWriter,System.String);;Argument[1];Argument[Qualifier];taint;generated", - "System.CodeDom.Compiler;IndentedTextWriter;false;WriteLineNoTabsAsync;(System.String);;Argument[0];ReturnValue;taint;generated", - "System.CodeDom.Compiler;IndentedTextWriter;false;WriteLineNoTabsAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom.Compiler;IndentedTextWriter;false;get_Encoding;();;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom.Compiler;IndentedTextWriter;false;get_InnerWriter;();;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom.Compiler;IndentedTextWriter;false;get_NewLine;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -746,7 +755,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.CodeDom;CodeNamespaceImport;false;set_Namespace;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.CodeDom;CodeNamespaceImportCollection;false;Add;(System.CodeDom.CodeNamespaceImport);;Argument[0];Argument[Qualifier];taint;generated", "System.CodeDom;CodeNamespaceImportCollection;false;AddRange;(System.CodeDom.CodeNamespaceImport[]);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.CodeDom;CodeNamespaceImportCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom;CodeNamespaceImportCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.CodeDom;CodeNamespaceImportCollection;false;set_Item;(System.Int32,System.CodeDom.CodeNamespaceImport);;Argument[1];Argument[Qualifier];taint;generated", "System.CodeDom;CodeObject;false;get_UserData;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -919,7 +927,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Concurrent;ConcurrentBag<>;false;TryAdd;(T);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.Concurrent;ConcurrentBag<>;false;TryPeek;(T);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Concurrent;ConcurrentBag<>;false;TryTake;(T);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetOrAdd;(TKey,TValue);;Argument[1];ReturnValue;taint;generated", "System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Concurrent;ConcurrentStack<>;false;ConcurrentStack;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", @@ -933,7 +940,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Concurrent;Partitioner;false;Create<>;(System.Collections.Generic.IEnumerable,System.Collections.Concurrent.EnumerablePartitionerOptions);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Concurrent;Partitioner;false;Create<>;(System.Collections.Generic.IList,System.Boolean);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Concurrent;Partitioner;false;Create<>;(TSource[],System.Boolean);;Argument[0].Element;ReturnValue;taint;generated", - "System.Collections.Generic;CollectionExtensions;false;AsReadOnly<,>;(System.Collections.Generic.IDictionary);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Generic;CollectionExtensions;false;AsReadOnly<>;(System.Collections.Generic.IList);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Generic;CollectionExtensions;false;GetDefaultAssets;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Generic;CollectionExtensions;false;GetDefaultGroup;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", @@ -945,9 +951,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[1];ReturnValue;taint;generated", "System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated", "System.Collections.Generic;CollectionExtensions;false;Remove<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;ReturnValue;taint;generated", - "System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;Argument[2];taint;generated", - "System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[1];Argument[0].Element;taint;generated", - "System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[2];Argument[0].Element;taint;generated", "System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Entry;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Key;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -967,8 +970,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Generic;HashSet<>;false;HashSet;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.Generic;HashSet<>;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Generic;HashSet<>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[1];ReturnValue;taint;generated", "System.Collections.Generic;KeyValuePair<,>;false;Deconstruct;(TKey,TValue);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Generic;KeyValuePair<,>;false;get_Key;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Generic;KeyValuePair<,>;false;get_Value;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -1120,12 +1121,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary;false;GetValueOrDefault<,>;(System.Collections.Immutable.IImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[0].Element;ReturnValue;taint;generated", @@ -1142,7 +1137,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableDictionary<,>+Builder;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>+Builder;false;set_KeyComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>+Builder;false;set_ValueComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated", - "System.Collections.Immutable;ImmutableDictionary<,>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>;false;Remove;(TKey);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>;false;RemoveRange;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>;false;SetItem;(TKey,TValue);;Argument[Qualifier];ReturnValue;taint;generated", @@ -1155,8 +1149,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableDictionary<,>;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableDictionary<,>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Collections.Immutable;ImmutableDictionary<,>;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableHashSet;false;Create<>;(System.Collections.Generic.IEqualityComparer,T);;Argument[1];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableHashSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet;false;ToImmutableHashSet<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", @@ -1166,7 +1158,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableHashSet<>+Builder;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>+Builder;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>+Builder;false;set_KeyComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated", - "System.Collections.Immutable;ImmutableHashSet<>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>;false;Except;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>;false;Intersect;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>;false;Remove;(T);;Argument[Qualifier];ReturnValue;taint;generated", @@ -1179,9 +1170,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableHashSet<>;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableHashSet<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Collections.Immutable;ImmutableInterlocked;false;GetOrAdd<,>;(System.Collections.Immutable.ImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableList;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableList;false;Create<>;(T[]);;Argument[0].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableList;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableList;false;Remove<>;(System.Collections.Immutable.IImmutableList,T);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableList;false;RemoveRange<>;(System.Collections.Immutable.IImmutableList,System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableList;false;Replace<>;(System.Collections.Immutable.IImmutableList,T,T);;Argument[0].Element;ReturnValue;taint;generated", @@ -1240,12 +1228,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[1];ReturnValue;taint;generated", @@ -1262,8 +1244,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_KeyComparer;(System.Collections.Generic.IComparer);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_ValueComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Remove;(TKey);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;RemoveRange;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;SetItem;(TKey,TValue);;Argument[0];ReturnValue;taint;generated", @@ -1281,10 +1261,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Collections.Immutable;ImmutableSortedDictionary<,>;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[1];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T[]);;Argument[0];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;CreateBuilder<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated", @@ -1294,7 +1271,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableSortedSet;false;ToImmutableSortedSet<>;(System.Collections.Generic.IEnumerable,System.Collections.Generic.IComparer);;Argument[1];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet;false;ToImmutableSortedSet<>;(System.Collections.Immutable.ImmutableSortedSet+Builder);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;IntersectWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;SymmetricExceptWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;ToImmutable;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated", @@ -1305,13 +1281,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;set_KeyComparer;(System.Collections.Generic.IComparer);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>;false;Clear;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>;false;Except;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>;false;Intersect;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>;false;Remove;(T);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated", - "System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>;false;ToBuilder;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated", "System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated", @@ -1335,23 +1306,17 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections.Immutable;ImmutableStack<>;false;Push;(T);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;Collection<>;false;Collection;(System.Collections.Generic.IList);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;Collection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;Collection<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;KeyedCollection;(System.Collections.Generic.IEqualityComparer,System.Int32);;Argument[0];Argument[Qualifier];taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;TryGetValue;(TKey,TItem);;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;KeyedCollection<,>;false;get_Dictionary;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.Collections.ObjectModel;ReadOnlyCollection<>;false;ReadOnlyCollection;(System.Collections.Generic.IList);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Collections.ObjectModel;ReadOnlyCollection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections.ObjectModel;ReadOnlyCollection<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -1430,7 +1395,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections;BitArray;false;Xor;(System.Collections.BitArray);;Argument[Qualifier];ReturnValue;value;generated", "System.Collections;BitArray;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[0];Argument[Qualifier];taint;generated", - "System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[Qualifier];Argument[0];taint;generated", "System.Collections;CollectionBase;false;get_InnerList;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections;CollectionBase;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections;CollectionBase;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -1480,7 +1444,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Collections;Stack;false;Synchronized;(System.Collections.Stack);;Argument[0].Element;ReturnValue;taint;generated", "System.Collections;Stack;false;ToArray;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Collections;Stack;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", - "System.ComponentModel.Composition.Hosting;AggregateCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AggregateCatalog;false;get_Catalogs;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AggregateExportProvider;false;AggregateExportProvider;(System.ComponentModel.Composition.Hosting.ExportProvider[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;AggregateExportProvider;false;GetExportsCore;(System.ComponentModel.Composition.Primitives.ImportDefinition,System.ComponentModel.Composition.Hosting.AtomicComposition);;Argument[Qualifier];ReturnValue;taint;generated", @@ -1489,7 +1452,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;ApplicationCatalog;(System.Reflection.ReflectionContext);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;ApplicationCatalog;(System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;ApplicationCatalog;(System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated", - "System.ComponentModel.Composition.Hosting;ApplicationCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.Reflection.Assembly);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.Reflection.Assembly,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.Reflection.Assembly,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated", @@ -1502,7 +1464,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.String,System.Reflection.ReflectionContext);;Argument[1];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;AssemblyCatalog;(System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[2];Argument[Qualifier];taint;generated", - "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;get_Assembly;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;AssemblyCatalog;false;get_DisplayName;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -1539,7 +1500,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;CompositionScopeDefinition;(System.ComponentModel.Composition.Primitives.ComposablePartCatalog,System.Collections.Generic.IEnumerable,System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;CompositionScopeDefinition;(System.ComponentModel.Composition.Primitives.ComposablePartCatalog,System.Collections.Generic.IEnumerable,System.Collections.Generic.IEnumerable);;Argument[1].Element;Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;CompositionScopeDefinition;(System.ComponentModel.Composition.Primitives.ComposablePartCatalog,System.Collections.Generic.IEnumerable,System.Collections.Generic.IEnumerable);;Argument[2].Element;Argument[Qualifier];taint;generated", - "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;get_Children;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;CompositionScopeDefinition;false;get_PublicSurface;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated", @@ -1554,7 +1514,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[2];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;DirectoryCatalog;(System.String,System.String,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[3];Argument[Qualifier];taint;generated", - "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;get_DisplayName;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;DirectoryCatalog;false;get_FullPath;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -1573,14 +1532,12 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;false;get_AddedExports;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;false;get_ChangedContractNames;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;ExportsChangeEventArgs;false;get_RemovedExports;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.ComponentModel.Composition.Hosting;FilteredCatalog;false;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;FilteredCatalog;false;get_Complement;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Hosting;ImportEngine;false;ImportEngine;(System.ComponentModel.Composition.Hosting.ExportProvider,System.ComponentModel.Composition.Hosting.CompositionOptions);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[1];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Hosting;TypeCatalog;false;TypeCatalog;(System.Collections.Generic.IEnumerable,System.Reflection.ReflectionContext,System.ComponentModel.Composition.Primitives.ICompositionElement);;Argument[2];Argument[Qualifier];taint;generated", - "System.ComponentModel.Composition.Primitives;ComposablePartCatalog;true;GetExports;(System.ComponentModel.Composition.Primitives.ImportDefinition);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Primitives;ComposablePartCatalog;true;get_Parts;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel.Composition.Primitives;ComposablePartException;false;ComposablePartException;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel.Composition.Primitives;ComposablePartException;false;ComposablePartException;(System.String,System.ComponentModel.Composition.Primitives.ICompositionElement,System.Exception);;Argument[1];Argument[Qualifier];taint;generated", @@ -1781,9 +1738,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[1];ReturnValue;taint;generated", "System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated", "System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated", - "System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated", "System.ComponentModel;CategoryAttribute;false;CategoryAttribute;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.ComponentModel;CategoryAttribute;false;get_Category;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel;CharConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated", @@ -1917,7 +1872,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.ComponentModel;ToolboxItemAttribute;false;get_ToolboxItemTypeName;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel;ToolboxItemFilterAttribute;false;get_TypeId;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel;TypeConverter+StandardValuesCollection;false;CopyTo;(System.Array,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated", - "System.ComponentModel;TypeConverter+StandardValuesCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel;TypeConverter+StandardValuesCollection;false;StandardValuesCollection;(System.Collections.ICollection);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.ComponentModel;TypeConverter+StandardValuesCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.ComponentModel;TypeConverter;false;ConvertFrom;(System.Object);;Argument[0];ReturnValue;taint;generated", @@ -2380,10 +2334,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Common;DataTableMappingCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Data.Common;DbCommand;false;ExecuteReader;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommand;false;ExecuteReader;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data.Common;DbCommand;false;ExecuteReaderAsync;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommand;false;get_Connection;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommand;false;get_Parameters;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommand;false;get_Transaction;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -2391,7 +2341,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Common;DbCommand;false;set_Connection;(System.Data.IDbConnection);;Argument[0];Argument[Qualifier];taint;generated", "System.Data.Common;DbCommand;false;set_Transaction;(System.Data.Common.DbTransaction);;Argument[0];Argument[Qualifier];taint;generated", "System.Data.Common;DbCommand;false;set_Transaction;(System.Data.IDbTransaction);;Argument[0];Argument[Qualifier];taint;generated", - "System.Data.Common;DbCommand;true;ExecuteDbDataReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommand;true;PrepareAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated", @@ -2418,7 +2367,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String);;Argument[2];Argument[0];taint;generated", "System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[1];Argument[0];taint;generated", "System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[2];Argument[0];taint;generated", - "System.Data.Common;DbConnectionStringBuilder;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbConnectionStringBuilder;false;GetProperties;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbConnectionStringBuilder;false;GetProperties;(System.Attribute[]);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbConnectionStringBuilder;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated", @@ -2445,12 +2393,9 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[0];ReturnValue;taint;generated", "System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[1];ReturnValue;taint;generated", "System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[3];ReturnValue;taint;generated", - "System.Data.Common;DbDataReader;false;GetFieldValueAsync<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbDataReader;true;GetFieldValue<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data.Common;DbDataReader;true;GetFieldValueAsync<>;(System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbDataReader;true;GetProviderSpecificValue;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbDataReader;true;GetProviderSpecificValues;(System.Object[]);;Argument[Qualifier];Argument[0].Element;taint;generated", - "System.Data.Common;DbDataReader;true;GetSchemaTableAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbDataReader;true;GetTextReader;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Common;DbDataRecord;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated", "System.Data.Common;DbEnumerator;false;DbEnumerator;(System.Data.IDataReader);;Argument[0];Argument[Qualifier];taint;generated", @@ -2595,7 +2540,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Odbc;OdbcParameter;false;set_Value;(System.Object);;Argument[0];Argument[Qualifier];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.Data.Odbc.OdbcParameter);;Argument[0];Argument[Qualifier];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.Data.Odbc.OdbcParameter);;Argument[0];ReturnValue;taint;generated", - "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.Data.Odbc.OdbcParameter);;Argument[Qualifier];Argument[0];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.String,System.Data.Odbc.OdbcType);;Argument[0];Argument[Qualifier];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.String,System.Data.Odbc.OdbcType);;Argument[0];ReturnValue;taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Add;(System.String,System.Data.Odbc.OdbcType,System.Int32);;Argument[0];Argument[Qualifier];taint;generated", @@ -2617,7 +2561,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data.Odbc;OdbcParameterCollection;false;GetParameter;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;GetParameter;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;Insert;(System.Int32,System.Data.Odbc.OdbcParameter);;Argument[1];Argument[Qualifier];taint;generated", - "System.Data.Odbc;OdbcParameterCollection;false;Insert;(System.Int32,System.Data.Odbc.OdbcParameter);;Argument[Qualifier];Argument[1];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;SetParameter;(System.Int32,System.Data.Common.DbParameter);;Argument[Qualifier];Argument[1];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;SetParameter;(System.String,System.Data.Common.DbParameter);;Argument[Qualifier];Argument[1];taint;generated", "System.Data.Odbc;OdbcParameterCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", @@ -2729,15 +2672,11 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data;DataColumn;false;set_Prefix;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataColumnChangeEventArgs;false;DataColumnChangeEventArgs;(System.Data.DataRow,System.Data.DataColumn,System.Object);;Argument[1];Argument[Qualifier];taint;generated", "System.Data;DataColumnChangeEventArgs;false;get_Column;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataColumnCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataColumnCollection;false;Add;(System.String,System.Type);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataColumnCollection;false;Add;(System.String,System.Type,System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataColumnCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataColumnCollection;false;get_Item;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataColumnCollection;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataReaderExtensions;false;GetDateTime;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated", "System.Data;DataReaderExtensions;false;GetFieldValue<>;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated", - "System.Data;DataReaderExtensions;false;GetFieldValueAsync<>;(System.Data.Common.DbDataReader,System.String,System.Threading.CancellationToken);;Argument[0].Element;ReturnValue;taint;generated", "System.Data;DataReaderExtensions;false;GetGuid;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated", "System.Data;DataReaderExtensions;false;GetProviderSpecificValue;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated", "System.Data;DataReaderExtensions;false;GetString;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated", @@ -2767,16 +2706,10 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data;DataRelation;false;get_RelationName;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataRelation;false;set_RelationName;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataRelationCollection;false;Remove;(System.Data.DataRelation);;Argument[0];Argument[Qualifier];taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];ReturnValue;taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];ReturnValue;taint;generated", - "System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataRow;false;DataRow;(System.Data.DataRowBuilder);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataRow;false;GetChildRows;(System.Data.DataRelation);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataRow;false;GetChildRows;(System.Data.DataRelation,System.Data.DataRowVersion);;Argument[Qualifier];ReturnValue;taint;generated", @@ -2874,10 +2807,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Data;DataTable;false;set_PrimaryKey;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Data;DataTable;false;set_Site;(System.ComponentModel.ISite);;Argument[0];Argument[Qualifier];taint;generated", "System.Data;DataTable;false;set_TableName;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.Data;DataTableCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated", "System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];ReturnValue;taint;generated", - "System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataTableCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataTableCollection;false;get_Item;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Data;DataTableCollection;false;get_Item;(System.String,System.String);;Argument[Qualifier];ReturnValue;taint;generated", @@ -3180,29 +3111,20 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.DirectoryServices.Protocols;DirSyncRequestControl;false;DirSyncRequestControl;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirSyncRequestControl;false;set_Cookie;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Byte[]);;Argument[Qualifier];Argument[0].Element;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.String);;Argument[Qualifier];Argument[0];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Uri);;Argument[0];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Add;(System.Uri);;Argument[Qualifier];Argument[0];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;AddRange;(System.Object[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;CopyTo;(System.Object[],System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;DirectoryAttribute;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;DirectoryAttribute;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;DirectoryAttribute;(System.String,System.Object[]);;Argument[Qualifier];Argument[1].Element;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;GetValues;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Byte[]);;Argument[1].Element;Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Byte[]);;Argument[Qualifier];Argument[1].Element;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.String);;Argument[1];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.String);;Argument[Qualifier];Argument[1];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Uri);;Argument[1];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Insert;(System.Int32,System.Uri);;Argument[Qualifier];Argument[1];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;Remove;(System.Object);;Argument[0];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;Remove;(System.Object);;Argument[Qualifier];Argument[0];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;get_Name;();;Argument[Qualifier];ReturnValue;taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;set_Item;(System.Int32,System.Object);;Argument[1];Argument[Qualifier];taint;generated", - "System.DirectoryServices.Protocols;DirectoryAttribute;false;set_Item;(System.Int32,System.Object);;Argument[Qualifier];Argument[1];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttribute;false;set_Name;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttributeCollection;false;Add;(System.DirectoryServices.Protocols.DirectoryAttribute);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.DirectoryServices.Protocols;DirectoryAttributeCollection;false;AddRange;(System.DirectoryServices.Protocols.DirectoryAttributeCollection);;Argument[0].Element;Argument[Qualifier];taint;generated", @@ -3391,22 +3313,18 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Drawing.Printing;PrintPageEventArgs;false;get_PageBounds;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrintPageEventArgs;false;get_PageSettings;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;Add;(System.Drawing.Printing.PaperSize);;Argument[0];Argument[Qualifier];taint;generated", - "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;PaperSizeCollection;(System.Drawing.Printing.PaperSize[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSizeCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;Add;(System.Drawing.Printing.PaperSource);;Argument[0];Argument[Qualifier];taint;generated", - "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;PaperSourceCollection;(System.Drawing.Printing.PaperSource[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PaperSourceCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;Add;(System.Drawing.Printing.PrinterResolution);;Argument[0];Argument[Qualifier];taint;generated", - "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;PrinterResolutionCollection;(System.Drawing.Printing.PrinterResolution[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+PrinterResolutionCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Drawing.Printing;PrinterSettings+StringCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.Drawing.Printing;PrinterSettings+StringCollection;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+StringCollection;false;StringCollection;(System.String[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Drawing.Printing;PrinterSettings+StringCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Drawing.Printing;PrinterSettings+StringCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", @@ -3663,14 +3581,10 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO.Compression;BrotliStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO.Compression;BrotliStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.Compression;DeflateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.IO.Compression;DeflateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO.Compression;DeflateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.Compression;DeflateStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.Compression;GZipStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO.Compression;GZipStream;false;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionLevel,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.IO.Compression;GZipStream;false;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", - "System.IO.Compression;GZipStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO.Compression;GZipStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.Compression;GZipStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.Compression;ZLibException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated", "System.IO.Compression;ZLibException;false;ZLibException;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[0];Argument[Qualifier];taint;generated", @@ -3709,7 +3623,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO.IsolatedStorage;IsolatedStorage;false;get_AssemblyIdentity;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.IsolatedStorage;IsolatedStorage;false;get_DomainIdentity;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO.IsolatedStorage;IsolatedStorageFileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", @@ -3830,7 +3743,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;BinaryReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated", - "System.IO;BinaryWriter;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;BinaryWriter;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;BinaryWriter;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;BinaryWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -3899,7 +3811,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;FileNotFoundException;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;FileNotFoundException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;FileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", @@ -3956,26 +3867,14 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;RenamedEventArgs;false;RenamedEventArgs;(System.IO.WatcherChangeTypes,System.String,System.String,System.String);;Argument[3];Argument[Qualifier];taint;generated", "System.IO;RenamedEventArgs;false;get_OldFullPath;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;RenamedEventArgs;false;get_OldName;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;Stream;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;Stream;false;Synchronized;(System.IO.Stream);;Argument[0];ReturnValue;taint;generated", - "System.IO;Stream;true;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;Stream;true;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;Stream;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated", "System.IO;StreamReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamReader;false;get_CurrentEncoding;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamWriter;false;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StreamWriter;false;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated", "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object,System.Object);;Argument[0];Argument[Qualifier];taint;generated", @@ -3987,15 +3886,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated", "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StreamWriter;false;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated", - "System.IO;StreamWriter;false;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StreamWriter;false;get_Encoding;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StringWriter;false;GetStringBuilder;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -4003,29 +3894,19 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;StringWriter;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;StringWriter;false;Write;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;Write;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.IO;StringWriter;false;Write;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;StringWriter;false;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated", - "System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextReader;false;Synchronized;(System.IO.TextReader);;Argument[0];ReturnValue;taint;generated", "System.IO;TextWriter;false;Synchronized;(System.IO.TextWriter);;Argument[0];ReturnValue;taint;generated", "System.IO;TextWriter;false;TextWriter;(System.IFormatProvider);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated", - "System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;Write;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;Write;(System.Object);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;Write;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated", @@ -4039,14 +3920,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;TextWriter;true;Write;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.IO;TextWriter;true;WriteLine;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;WriteLine;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", @@ -4063,18 +3937,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.IO;TextWriter;true;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated", "System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated", - "System.IO;TextWriter;true;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", - "System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;get_FormatProvider;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;get_NewLine;();;Argument[Qualifier];ReturnValue;taint;generated", "System.IO;TextWriter;true;set_NewLine;(System.String);;Argument[0];Argument[Qualifier];taint;generated", @@ -4673,7 +4537,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Http.Headers;HeaderStringValues;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http.Headers;HttpHeaders;false;get_NonValidated;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http.Headers;HttpHeadersNonValidated+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http.Headers;HttpHeadersNonValidated;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http.Headers;HttpHeadersNonValidated;false;TryGetValue;(System.String,System.Net.Http.Headers.HeaderStringValues);;Argument[0];ReturnValue;taint;generated", "System.Net.Http.Headers;HttpHeadersNonValidated;false;TryGetValues;(System.String,System.Net.Http.Headers.HeaderStringValues);;Argument[0];ReturnValue;taint;generated", "System.Net.Http.Headers;HttpHeadersNonValidated;false;get_Item;(System.String);;Argument[0];ReturnValue;taint;generated", @@ -4774,23 +4637,14 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Net.Http;ByteArrayContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;ByteArrayContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;ByteArrayContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated", "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;DelegatingHandler;false;DelegatingHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Http;DelegatingHandler;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Http;DelegatingHandler;false;get_InnerHandler;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;DelegatingHandler;false;set_InnerHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated", - "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage);;Argument[Qualifier];Argument[0];taint;generated", "System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption);;Argument[Qualifier];Argument[0];taint;generated", "System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", @@ -4817,10 +4671,7 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Http;HttpContent;false;ReadAsStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;HttpContent;false;get_Headers;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;HttpContent;true;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;HttpContent;true;CreateContentReadStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;HttpMessageInvoker;false;HttpMessageInvoker;(System.Net.Http.HttpMessageHandler,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Http;HttpMessageInvoker;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Http;HttpMethod;false;HttpMethod;(System.String);;Argument[0];Argument[Qualifier];taint;generated", @@ -4855,7 +4706,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Http;MultipartFormDataContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", "System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;ReadOnlyMemoryContent;false;ReadOnlyMemoryContent;(System.ReadOnlyMemory);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Http;SocketsHttpConnectionContext;false;get_DnsEndPoint;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;SocketsHttpConnectionContext;false;get_InitialRequestMessage;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -4894,19 +4744,11 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_NegotiatedHttpVersion;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_PlaintextStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;StreamContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated", "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream,System.Int32);;Argument[0];Argument[Qualifier];taint;generated", - "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String);;Argument[0];ReturnValue;taint;generated", "System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[0];ReturnValue;taint;generated", "System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[1];ReturnValue;taint;generated", @@ -5039,7 +4881,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Quic;QuicListener;false;QuicListener;(System.Net.Quic.Implementations.QuicImplementationProvider,System.Net.Quic.QuicListenerOptions);;Argument[1];Argument[Qualifier];taint;generated", "System.Net.Quic;QuicListener;false;get_ListenEndPoint;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;AuthenticatedStream;false;AuthenticatedStream;(System.IO.Stream,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", - "System.Net.Security;AuthenticatedStream;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;AuthenticatedStream;false;get_InnerStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[1];Argument[Qualifier];taint;generated", "System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[2];Argument[Qualifier];taint;generated", @@ -5058,7 +4899,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Security;NegotiateStream;false;AuthenticateAsServerAsync;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy,System.Net.Security.ProtectionLevel,System.Security.Principal.TokenImpersonationLevel);;Argument[1];Argument[Qualifier];taint;generated", "System.Net.Security;NegotiateStream;false;AuthenticateAsServerAsync;(System.Security.Authentication.ExtendedProtection.ExtendedProtectionPolicy);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", @@ -5071,7 +4911,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Security;SslCertificateTrust;false;CreateForX509Collection;(System.Security.Cryptography.X509Certificates.X509Certificate2Collection,System.Boolean);;Argument[0].Element;ReturnValue;taint;generated", "System.Net.Security;SslCertificateTrust;false;CreateForX509Store;(System.Security.Cryptography.X509Certificates.X509Store,System.Boolean);;Argument[0];ReturnValue;taint;generated", "System.Net.Security;SslStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Net.Security;SslStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;SslStream;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Net.Security;SslStream;false;get_LocalCertificate;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Security;SslStream;false;get_NegotiatedApplicationProtocol;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -5102,7 +4941,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;NetworkStream;false;get_Socket;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Sockets;SafeSocketHandle;false;SafeSocketHandle;(System.IntPtr,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Sockets;Socket;false;Accept;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;AcceptAsync;(System.Net.Sockets.Socket,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", @@ -5126,7 +4964,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;Socket;false;DisconnectAsync;(System.Boolean,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;DisconnectAsync;(System.Boolean,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;DisconnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated", - "System.Net.Sockets;Socket;false;EndAccept;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveAsync;(System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveAsync;(System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveAsync;(System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", @@ -5146,8 +4983,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;Socket;false;ReceiveFrom;(System.Span,System.Net.EndPoint);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];Argument[Qualifier];taint;generated", "System.Net.Sockets;Socket;false;ReceiveFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];ReturnValue;taint;generated", - "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.ArraySegment,System.Net.EndPoint);;Argument[1];ReturnValue;taint;generated", - "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", @@ -5161,8 +4996,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;Socket;false;ReceiveMessageFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint,System.Net.Sockets.IPPacketInformation);;Argument[4];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveMessageFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint,System.Net.Sockets.IPPacketInformation);;Argument[2];Argument[Qualifier];taint;generated", "System.Net.Sockets;Socket;false;ReceiveMessageFrom;(System.Span,System.Net.Sockets.SocketFlags,System.Net.EndPoint,System.Net.Sockets.IPPacketInformation);;Argument[2];ReturnValue;taint;generated", - "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.ArraySegment,System.Net.EndPoint);;Argument[1];ReturnValue;taint;generated", - "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[2];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;Socket;false;ReceiveMessageFromAsync;(System.Memory,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated", @@ -5221,7 +5054,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;SocketAsyncEventArgs;false;set_SendPacketsElements;(System.Net.Sockets.SendPacketsElement[]);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Net.Sockets;SocketAsyncEventArgs;false;set_UserToken;(System.Object);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Sockets;SocketException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Sockets;SocketTaskExtensions;false;AcceptAsync;(System.Net.Sockets.Socket,System.Net.Sockets.Socket);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint);;Argument[1];Argument[0];taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated", @@ -5233,8 +5065,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated", - "System.Net.Sockets;SocketTaskExtensions;false;ReceiveFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated", - "System.Net.Sockets;SocketTaskExtensions;false;ReceiveMessageFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated", "System.Net.Sockets;SocketTaskExtensions;false;SendToAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];Argument[0];taint;generated", @@ -5249,8 +5079,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net.Sockets;TcpListener;false;AcceptSocketAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;TcpListener;false;AcceptSocketAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net.Sockets;TcpListener;false;AcceptTcpClient;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net.Sockets;TcpListener;false;EndAcceptSocket;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net.Sockets;TcpListener;false;EndAcceptTcpClient;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", "System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPAddress,System.Int32);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPEndPoint);;Argument[0];Argument[Qualifier];taint;generated", "System.Net.Sockets;TcpListener;false;get_LocalEndpoint;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -5335,17 +5163,11 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net;CookieCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated", "System.Net;CookieException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated", "System.Net;CredentialCache;false;GetCredential;(System.Uri,System.String);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net;Dns;false;EndGetHostAddresses;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;Dns;false;EndGetHostByName;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;Dns;false;EndGetHostEntry;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;Dns;false;EndResolve;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", "System.Net;DnsEndPoint;false;DnsEndPoint;(System.String,System.Int32,System.Net.Sockets.AddressFamily);;Argument[0];Argument[Qualifier];taint;generated", "System.Net;DnsEndPoint;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;DnsEndPoint;false;get_Host;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;DownloadDataCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;DownloadStringCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net;FileWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;FileWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", "System.Net;FileWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;FileWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;FileWebRequest;false;get_ContentType;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -5419,9 +5241,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net;HttpListenerTimeoutManager;false;get_IdleConnection;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;HttpListenerTimeoutManager;false;set_DrainEntityBody;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated", "System.Net;HttpListenerTimeoutManager;false;set_IdleConnection;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated", - "System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated", - "System.Net;HttpWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", "System.Net;HttpWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;HttpWebRequest;false;GetRequestStream;(System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;HttpWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -5478,7 +5297,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net;OpenWriteCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;PathList;false;get_Item;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;PathList;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Net;PathList;false;get_Values;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;ProtocolViolationException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated", "System.Net;UploadDataCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Net;UploadFileCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -5508,8 +5326,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest);;Argument[0];ReturnValue;taint;generated", "System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];Argument[Qualifier];taint;generated", "System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];ReturnValue;taint;generated", - "System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];Argument[Qualifier];taint;generated", - "System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];ReturnValue;taint;generated", "System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];ReturnValue;taint;generated", "System.Net;WebClient;false;OpenRead;(System.String);;Argument[Qualifier];ReturnValue;taint;generated", @@ -6425,7 +6241,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Resources;ResourceReader;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Resources;ResourceReader;false;GetResourceData;(System.String,System.String,System.Byte[]);;Argument[Qualifier];ReturnValue;taint;generated", "System.Resources;ResourceReader;false;ResourceReader;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated", - "System.Resources;ResourceSet;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Resources;ResourceSet;false;ResourceSet;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated", "System.Resources;ResourceSet;false;ResourceSet;(System.Resources.IResourceReader);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Resources;ResourceWriter;false;ResourceWriter;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated", @@ -6836,7 +6651,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Security.Cryptography.Pkcs;Pkcs12SafeBag;false;set_Attributes;(System.Security.Cryptography.CryptographicAttributeObjectCollection);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Security.Cryptography.Pkcs;Pkcs12SafeContents;false;AddSafeBag;(System.Security.Cryptography.Pkcs.Pkcs12SafeBag);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography.Pkcs;Pkcs12SafeContents;false;AddSecret;(System.Security.Cryptography.Oid,System.ReadOnlyMemory);;Argument[0];ReturnValue;taint;generated", - "System.Security.Cryptography.Pkcs;Pkcs12SafeContents;false;GetBags;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Pkcs;Pkcs12SecretBag;false;GetSecretType;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Pkcs;Pkcs12SecretBag;false;get_SecretValue;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Pkcs;Pkcs9AttributeObject;false;CopyFrom;(System.Security.Cryptography.AsnEncodedData);;Argument[0];Argument[Qualifier];taint;generated", @@ -6923,7 +6737,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Security.Cryptography.X509Certificates;X509Certificate;false;ToString;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Issuer;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Subject;();;Argument[Qualifier];ReturnValue;taint;generated", - "System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;X509CertificateEnumerator;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;Remove;(System.Security.Cryptography.X509Certificates.X509Certificate);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;X509CertificateCollection;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier];taint;generated", @@ -7044,7 +6857,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Security.Cryptography.Xml;EncryptionPropertyCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Xml;EncryptionPropertyCollection;false;set_ItemOf;(System.Int32,System.Security.Cryptography.Xml.EncryptionProperty);;Argument[1];Argument[Qualifier];taint;generated", "System.Security.Cryptography.Xml;KeyInfo;false;AddClause;(System.Security.Cryptography.Xml.KeyInfoClause);;Argument[0];Argument[Qualifier];taint;generated", - "System.Security.Cryptography.Xml;KeyInfo;false;GetEnumerator;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Xml;KeyInfo;false;LoadXml;(System.Xml.XmlElement);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Security.Cryptography.Xml;KeyInfo;false;get_Id;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Xml;KeyInfo;false;set_Id;(System.String);;Argument[0];Argument[Qualifier];taint;generated", @@ -7157,7 +6969,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Security.Cryptography.Xml;Transform;false;set_Context;(System.Xml.XmlElement);;Argument[0].Element;Argument[Qualifier];taint;generated", "System.Security.Cryptography.Xml;Transform;false;set_Resolver;(System.Xml.XmlResolver);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography.Xml;TransformChain;false;Add;(System.Security.Cryptography.Xml.Transform);;Argument[0];Argument[Qualifier];taint;generated", - "System.Security.Cryptography.Xml;TransformChain;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Xml;TransformChain;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography.Xml;XmlDecryptionTransform;false;AddExceptUri;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography.Xml;XmlDecryptionTransform;false;GetOutput;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -7223,7 +7034,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Security.Cryptography;CryptoStream;false;CryptoStream;(System.IO.Stream,System.Security.Cryptography.ICryptoTransform,System.Security.Cryptography.CryptoStreamMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography;CryptoStream;false;CryptoStream;(System.IO.Stream,System.Security.Cryptography.ICryptoTransform,System.Security.Cryptography.CryptoStreamMode,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated", "System.Security.Cryptography;CryptoStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Security.Cryptography;CryptoStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography;CryptographicAttributeObject;false;CryptographicAttributeObject;(System.Security.Cryptography.Oid,System.Security.Cryptography.AsnEncodedDataCollection);;Argument[0];Argument[Qualifier];taint;generated", "System.Security.Cryptography;CryptographicAttributeObject;false;get_Oid;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Security.Cryptography;CryptographicAttributeObjectCollection;false;Add;(System.Security.Cryptography.CryptographicAttributeObject);;Argument[0];Argument[Qualifier];taint;generated", @@ -7488,7 +7298,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Text.Encodings.Web;TextEncoder;true;Encode;(System.IO.TextWriter,System.String,System.Int32,System.Int32);;Argument[1];Argument[0];taint;generated", "System.Text.Encodings.Web;TextEncoder;true;Encode;(System.String);;Argument[0];ReturnValue;taint;generated", "System.Text.Json.Nodes;JsonArray;false;Add<>;(T);;Argument[0];Argument[Qualifier];taint;generated", - "System.Text.Json.Nodes;JsonArray;false;Add<>;(T);;Argument[Qualifier];Argument[0];taint;generated", "System.Text.Json.Nodes;JsonArray;false;Create;(System.Text.Json.JsonElement,System.Nullable);;Argument[0];ReturnValue;taint;generated", "System.Text.Json.Nodes;JsonArray;false;JsonArray;(System.Text.Json.Nodes.JsonNodeOptions,System.Text.Json.Nodes.JsonNode[]);;Argument[Qualifier];Argument[1].Element;taint;generated", "System.Text.Json.Nodes;JsonArray;false;JsonArray;(System.Text.Json.Nodes.JsonNode[]);;Argument[Qualifier];Argument[0].Element;taint;generated", @@ -7508,7 +7317,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Text.Json.Serialization;JsonSerializerContext;false;JsonSerializerContext;(System.Text.Json.JsonSerializerOptions);;Argument[Qualifier];Argument[0];taint;generated", "System.Text.Json.Serialization;JsonSerializerContext;false;get_Options;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Text.Json.Serialization;JsonStringEnumConverter;false;JsonStringEnumConverter;(System.Text.Json.JsonNamingPolicy,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", - "System.Text.Json.SourceGeneration;JsonSourceGenerator;false;GetSerializableTypes;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Text.Json;JsonDocument;false;Parse;(System.Buffers.ReadOnlySequence,System.Text.Json.JsonDocumentOptions);;Argument[0];ReturnValue;taint;generated", "System.Text.Json;JsonDocument;false;Parse;(System.IO.Stream,System.Text.Json.JsonDocumentOptions);;Argument[0];ReturnValue;taint;generated", "System.Text.Json;JsonDocument;false;Parse;(System.ReadOnlyMemory,System.Text.Json.JsonDocumentOptions);;Argument[0];ReturnValue;taint;generated", @@ -7733,7 +7541,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Threading.RateLimiting;MetadataName<>;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Threading.RateLimiting;MetadataName<>;false;get_Name;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Threading.RateLimiting;RateLimitLease;false;TryGetMetadata<>;(System.Threading.RateLimiting.MetadataName,T);;Argument[Qualifier];ReturnValue;taint;generated", - "System.Threading.RateLimiting;RateLimitLease;true;GetAllMetadata;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Threading.RateLimiting;RateLimiter;false;Acquire;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated", "System.Threading.RateLimiting;RateLimiter;false;WaitAsync;(System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated", "System.Threading.RateLimiting;TokenBucketRateLimiter;false;TokenBucketRateLimiter;(System.Threading.RateLimiting.TokenBucketRateLimiterOptions);;Argument[0];Argument[Qualifier];taint;generated", @@ -7775,10 +7582,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated", "System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock);;Argument[0];ReturnValue;taint;generated", - "System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", - "System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated", - "System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated", "System.Threading.Tasks.Dataflow;DataflowBlock;false;SendAsync<>;(System.Threading.Tasks.Dataflow.ITargetBlock,TInput,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated", "System.Threading.Tasks.Dataflow;DataflowBlock;false;TryReceive<>;(System.Threading.Tasks.Dataflow.IReceivableSourceBlock,TOutput);;Argument[0];ReturnValue;taint;generated", "System.Threading.Tasks.Dataflow;DataflowBlockOptions;false;get_CancellationToken;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -8374,12 +8177,9 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Xml.Schema;XmlSchemaSet;false;Add;(System.String,System.Xml.XmlReader);;Argument[Qualifier];ReturnValue;taint;generated", "System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated", "System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated", - "System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchemaSet);;Argument[0];Argument[Qualifier];taint;generated", - "System.Xml.Schema;XmlSchemaSet;false;CopyTo;(System.Xml.Schema.XmlSchema[],System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated", "System.Xml.Schema;XmlSchemaSet;false;Remove;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated", "System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated", "System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated", - "System.Xml.Schema;XmlSchemaSet;false;Schemas;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Xml.Schema;XmlSchemaSet;false;XmlSchemaSet;(System.Xml.XmlNameTable);;Argument[0];Argument[Qualifier];taint;generated", "System.Xml.Schema;XmlSchemaSet;false;get_CompilationSettings;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Xml.Schema;XmlSchemaSet;false;get_GlobalAttributes;();;Argument[Qualifier];ReturnValue;taint;generated", @@ -8466,10 +8266,8 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Xml.Schema;XmlSchemaXPath;false;get_XPath;();;Argument[Qualifier];ReturnValue;taint;generated", "System.Xml.Schema;XmlSchemaXPath;false;set_XPath;(System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated", "System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[0];ReturnValue;taint;generated", "System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated", "System.Xml.Serialization;CodeIdentifiers;false;MakeUnique;(System.String);;Argument[0];ReturnValue;taint;generated", "System.Xml.Serialization;CodeIdentifiers;false;ToArray;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated", "System.Xml.Serialization;ImportContext;false;ImportContext;(System.Xml.Serialization.CodeIdentifiers,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", @@ -8752,7 +8550,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.Xml.XmlQualifiedName);;Argument[1];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WriteId;(System.Object);;Argument[Qualifier];Argument[0];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncoded;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.Byte[],System.Xml.XmlQualifiedName);;Argument[2].Element;Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated", @@ -8760,15 +8557,9 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.Byte[]);;Argument[2].Element;Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[2];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[2];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[Qualifier];Argument[2];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated", - "System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated", "System.Xml.Serialization;XmlSerializationWriter;false;WriteSerializable;(System.Xml.Serialization.IXmlSerializable,System.String,System.String,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated", @@ -9945,27 +9736,6 @@ private class RuntimeSummaryCsv extends SummaryModelCsv { "System;Tuple<,>;false;get_Item2;();;Argument[Qualifier];ReturnValue;taint;generated", "System;Tuple<>;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated", "System;Tuple<>;false;get_Item1;();;Argument[Qualifier];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", - "System;TupleExtensions;false;ToTuple<>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated", "System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated", "System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated", "System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated", diff --git a/csharp/ql/lib/semmle/code/csharp/frameworks/system/security/Cryptography.qll b/csharp/ql/lib/semmle/code/csharp/frameworks/system/security/Cryptography.qll index 156f99d7fde..aad7e601249 100644 --- a/csharp/ql/lib/semmle/code/csharp/frameworks/system/security/Cryptography.qll +++ b/csharp/ql/lib/semmle/code/csharp/frameworks/system/security/Cryptography.qll @@ -42,3 +42,23 @@ private class SystemSecurityCryptographyOidCollectionFlowModelCsv extends Summar ] } } + +/** Sinks for `System.Security.Cryptography`. */ +private class SystemSecurityCryptographySinkModelCsv extends SinkModelCsv { + override predicate row(string row) { + row = + [ + "System.Security.Cryptography;SymmetricAlgorithm;true;CreateEncryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-encryptor;manual", + "System.Security.Cryptography;SymmetricAlgorithm;true;CreateDecryptor;(System.Byte[],System.Byte[]);;Argument[0];encryption-decryptor;manual", + "System.Security.Cryptography;SymmetricAlgorithm;true;set_Key;(System.Byte[]);;Argument[0];encryption-keyprop;manual", + ] + } +} + +/** Sinks for `Windows.Security.Cryptography.Core`. */ +private class WindowsSecurityCryptographyCoreSinkModelCsv extends SinkModelCsv { + override predicate row(string row) { + row = + "Windows.Security.Cryptography.Core;SymmetricKeyAlgorithmProvider;false;CreateSymmetricKey;(Windows.Storage.Streams.IBuffer);;Argument[0];encryption-symmetrickey;manual" + } +} diff --git a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll index 3cf3fa107bf..e937e69919e 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/cryptography/HardcodedSymmetricEncryptionKey.qll @@ -4,6 +4,7 @@ */ import csharp +private import semmle.code.csharp.dataflow.ExternalFlow module HardcodedSymmetricEncryptionKey { private import semmle.code.csharp.frameworks.system.security.cryptography.SymmetricAlgorithm @@ -38,45 +39,20 @@ module HardcodedSymmetricEncryptionKey { StringLiteralSource() { this.asExpr() instanceof StringLiteral } } - private class SymmetricEncryptionKeyPropertySink extends Sink { - SymmetricEncryptionKeyPropertySink() { - this.asExpr() = any(SymmetricAlgorithm sa).getKeyProperty().getAnAssignedValue() + private class SymmetricAlgorithmSink extends Sink { + private string kind; + + SymmetricAlgorithmSink() { sinkNode(this, kind) and kind.matches("encryption%") } + + override string getDescription() { + kind = "encryption-encryptor" and result = "Encryptor(rgbKey, IV)" + or + kind = "encryption-decryptor" and result = "Decryptor(rgbKey, IV)" + or + kind = "encryption-symmetrickey" and result = "CreateSymmetricKey(IBuffer keyMaterial)" + or + kind = "encryption-keyprop" and result = "'Key' property assignment" } - - override string getDescription() { result = "'Key' property assignment" } - } - - private class SymmetricEncryptionCreateEncryptorSink extends Sink { - SymmetricEncryptionCreateEncryptorSink() { - exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricEncryptor() | - this.asExpr() = mc.getArgumentForName("rgbKey") - ) - } - - override string getDescription() { result = "Encryptor(rgbKey, IV)" } - } - - private class SymmetricEncryptionCreateDecryptorSink extends Sink { - SymmetricEncryptionCreateDecryptorSink() { - exists(SymmetricAlgorithm ag, MethodCall mc | mc = ag.getASymmetricDecryptor() | - this.asExpr() = mc.getArgumentForName("rgbKey") - ) - } - - override string getDescription() { result = "Decryptor(rgbKey, IV)" } - } - - private class CreateSymmetricKeySink extends Sink { - CreateSymmetricKeySink() { - exists(MethodCall mc, Method m | - mc.getTarget() = m and - m.hasQualifiedName("Windows.Security.Cryptography.Core.SymmetricKeyAlgorithmProvider", - "CreateSymmetricKey") and - this.asExpr() = mc.getArgumentForName("keyMaterial") - ) - } - - override string getDescription() { result = "CreateSymmetricKey(IBuffer keyMaterial)" } } private class CryptographicBuffer extends Class { diff --git a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll index c45d764701e..241f16d06ed 100644 --- a/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll +++ b/csharp/ql/lib/semmle/code/csharp/security/dataflow/flowsources/Remote.qll @@ -172,19 +172,19 @@ class ActionMethodParameter extends RemoteFlowSource, DataFlow::ParameterNode { abstract class AspNetCoreRemoteFlowSource extends RemoteFlowSource { } /** - * Data flow for AST.NET Core. + * Data flow for ASP.NET Core. * * Flow is defined from any ASP.NET Core remote source object to any of its member * properties. */ -private class AspNetCoreRemoteFlowSourceMember extends TaintTracking::TaintedMember { +private class AspNetCoreRemoteFlowSourceMember extends TaintTracking::TaintedMember, Property { AspNetCoreRemoteFlowSourceMember() { this.getDeclaringType() = any(AspNetCoreRemoteFlowSource source).getType() and this.isPublic() and not this.isStatic() and - exists(Property p | p = this | - p.isAutoImplemented() and p.getGetter().isPublic() and p.getSetter().isPublic() - ) + this.isAutoImplemented() and + this.getGetter().isPublic() and + this.getSetter().isPublic() } } diff --git a/csharp/ql/src/CHANGELOG.md b/csharp/ql/src/CHANGELOG.md index e7ce0b0b471..bb530ba1727 100644 --- a/csharp/ql/src/CHANGELOG.md +++ b/csharp/ql/src/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.3.1 + +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/csharp-all` package. + ## 0.2.0 ### Query Metadata Changes diff --git a/csharp/ql/src/Telemetry/ExternalApi.qll b/csharp/ql/src/Telemetry/ExternalApi.qll index 9679cd1061a..685eda64e50 100644 --- a/csharp/ql/src/Telemetry/ExternalApi.qll +++ b/csharp/ql/src/Telemetry/ExternalApi.qll @@ -85,7 +85,7 @@ class ExternalApi extends DotNet::Callable { defaultAdditionalTaintStep(this.getAnInput(), _) } - /** Holds if this API is is a constructor without parameters. */ + /** Holds if this API is a constructor without parameters. */ private predicate isParameterlessConstructor() { this instanceof Constructor and this.getNumberOfParameters() = 0 } diff --git a/csharp/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/csharp/ql/src/change-notes/released/0.3.0.md similarity index 77% rename from csharp/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename to csharp/ql/src/change-notes/released/0.3.0.md index a27c68766c0..001d034c251 100644 --- a/csharp/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ b/csharp/ql/src/change-notes/released/0.3.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 0.3.0 + +### Breaking Changes + * Contextual queries and the query libraries they depend on have been moved to the `codeql/csharp-all` package. diff --git a/csharp/ql/src/change-notes/released/0.3.1.md b/csharp/ql/src/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..2b0719929a1 --- /dev/null +++ b/csharp/ql/src/change-notes/released/0.3.1.md @@ -0,0 +1 @@ +## 0.3.1 diff --git a/csharp/ql/src/codeql-pack.release.yml b/csharp/ql/src/codeql-pack.release.yml index 5274e27ed52..bb106b1cb63 100644 --- a/csharp/ql/src/codeql-pack.release.yml +++ b/csharp/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.1 diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs new file mode 100644 index 00000000000..bee97118ea8 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.cs @@ -0,0 +1,44 @@ + +{ + SymmetricKey aesKey = new SymmetricKey(kid: "symencryptionkey"); + + // BAD: Using the outdated client side encryption version V1_0 + BlobEncryptionPolicy uploadPolicy = new BlobEncryptionPolicy(key: aesKey, keyResolver: null); + BlobRequestOptions uploadOptions = new BlobRequestOptions() { EncryptionPolicy = uploadPolicy }; + + MemoryStream stream = new MemoryStream(buffer); + blob.UploadFromStream(stream, length: size, accessCondition: null, options: uploadOptions); +} + +var client = new BlobClient(myConnectionString, new SpecializedBlobClientOptions() +{ + // BAD: Using an outdated SDK that does not support client side encryption version V2_0 + ClientSideEncryption = new ClientSideEncryptionOptions() + { + KeyEncryptionKey = myKey, + KeyResolver = myKeyResolver, + KeyWrapAlgorihm = myKeyWrapAlgorithm + } +}); + +var client = new BlobClient(myConnectionString, new SpecializedBlobClientOptions() +{ + // BAD: Using the outdated client side encryption version V1_0 + ClientSideEncryption = new ClientSideEncryptionOptions(ClientSideEncryptionVersion.V1_0) + { + KeyEncryptionKey = myKey, + KeyResolver = myKeyResolver, + KeyWrapAlgorihm = myKeyWrapAlgorithm + } +}); + +var client = new BlobClient(myConnectionString, new SpecializedBlobClientOptions() +{ + // GOOD: Using client side encryption version V2_0 + ClientSideEncryption = new ClientSideEncryptionOptions(ClientSideEncryptionVersion.V2_0) + { + KeyEncryptionKey = myKey, + KeyResolver = myKeyResolver, + KeyWrapAlgorihm = myKeyWrapAlgorithm + } +}); \ No newline at end of file diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp new file mode 100644 index 00000000000..54c9a4998b4 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -0,0 +1,29 @@ + + + + + +

    Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.

    +

    Current release versions of the Azure Storage SDKs use cipher block chaining (CBC mode) for client-side encryption (referred to as v1).

    + +
    + + +

    Consider switching to v2 client-side encryption.

    + +
    + + + + + + +
  • + Azure Storage Client Encryption Blog. +
  • +
  • + CVE-2022-30187 +
  • + +
    +
    diff --git a/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql new file mode 100644 index 00000000000..eb1cb673ed2 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -0,0 +1,81 @@ +/** + * @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-30187). + * @description Unsafe usage of v1 version of Azure Storage client-side encryption, please refer to http://aka.ms/azstorageclientencryptionblog + * @kind problem + * @tags security + * cryptography + * external/cwe/cwe-327 + * @id cs/azure-storage/unsafe-usage-of-client-side-encryption-version + * @problem.severity error + * @precision high + */ + +import csharp + +/** + * Holds if `oc` is creating an object of type `c` = `Azure.Storage.ClientSideEncryptionOptions` + * and `e` is the `version` argument to the constructor + */ +predicate isCreatingAzureClientSideEncryptionObject(ObjectCreation oc, Class c, Expr e) { + exists(Parameter p | p.hasName("version") | + c.hasQualifiedName("Azure.Storage.ClientSideEncryptionOptions") and + oc.getTarget() = c.getAConstructor() and + e = oc.getArgumentForParameter(p) + ) +} + +/** + * Holds if `oc` is an object creation of the outdated type `c` = `Microsoft.Azure.Storage.Blob.BlobEncryptionPolicy` + */ +predicate isCreatingOutdatedAzureClientSideEncryptionObject(ObjectCreation oc, Class c) { + c.hasQualifiedName("Microsoft.Azure.Storage.Blob.BlobEncryptionPolicy") and + oc.getTarget() = c.getAConstructor() +} + +/** + * Holds if the Azure.Storage assembly for `c` is a version known to support + * version 2+ for client-side encryption + */ +predicate doesAzureStorageAssemblySupportSafeClientSideEncryption(Assembly asm) { + exists(int versionCompare | + versionCompare = asm.getVersion().compareTo("12.12.0.0") and + versionCompare >= 0 + ) and + asm.getName() = "Azure.Storage.Common" +} + +/** + * Holds if the Azure.Storage assembly for `c` is a version known to support + * version 2+ for client-side encryption and if the argument for the constructor `version` + * is set to a secure value. + */ +predicate isObjectCreationArgumentSafeAndUsingSafeVersionOfAssembly(Expr versionExpr, Assembly asm) { + // Check if the Azure.Storage assembly version has the fix + doesAzureStorageAssemblySupportSafeClientSideEncryption(asm) and + // and that the version argument for the constructor is guaranteed to be Version2 + isExprAnAccessToSafeClientSideEncryptionVersionValue(versionExpr) +} + +/** + * Holds if the expression `e` is an access to a safe version of the enum `ClientSideEncryptionVersion` + * or an equivalent numeric value + */ +predicate isExprAnAccessToSafeClientSideEncryptionVersionValue(Expr e) { + exists(EnumConstant ec | + ec.hasQualifiedName("Azure.Storage.ClientSideEncryptionVersion.V2_0") and + ec.getAnAccess() = e + ) +} + +from Expr e, Class c, Assembly asm +where + asm = c.getLocation() and + ( + exists(Expr e2 | + isCreatingAzureClientSideEncryptionObject(e, c, e2) and + not isObjectCreationArgumentSafeAndUsingSafeVersionOfAssembly(e2, asm) + ) + or + isCreatingOutdatedAzureClientSideEncryptionObject(e, c) + ) +select e, "Unsafe usage of v1 version of Azure Storage client-side encryption." diff --git a/csharp/ql/src/qlpack.yml b/csharp/ql/src/qlpack.yml index 1002c6a56ad..9f59ceafaf5 100644 --- a/csharp/ql/src/qlpack.yml +++ b/csharp/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/csharp-queries -version: 0.2.1-dev +version: 0.3.2-dev groups: - csharp - queries diff --git a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected index b6d827ca880..2d9fce246fb 100644 --- a/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected +++ b/csharp/ql/test/library-tests/dataflow/library/FlowSummaries.expected @@ -336,7 +336,6 @@ | System.Collections.Concurrent;ConcurrentDictionary<,>;false;CopyTo;(System.Collections.Generic.KeyValuePair[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Generic.IEnumerator<>.Current];value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | -| System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetOrAdd;(TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Item;(System.Object);;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue;value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Item;(TKey);;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue;value;manual | @@ -370,9 +369,6 @@ | System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | | System.Collections.Generic;CollectionExtensions;false;Remove<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;Argument[2];taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[1];Argument[0].Element;taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[2];Argument[0].Element;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Entry;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Key;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -451,8 +447,6 @@ | System.Collections.Generic;IList<>;true;get_Item;(System.Int32);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.Generic;IList<>;true;set_Item;(System.Int32,T);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.Generic;ISet<>;true;Add;(T);;Argument[0];Argument[Qualifier].Element;value;manual | -| System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Generic;KeyValuePair<,>;false;Deconstruct;(TKey,TValue);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;KeyValuePair<,>;false;KeyValuePair;(TKey,TValue);;Argument[0];ReturnValue.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Generic;KeyValuePair<,>;false;KeyValuePair;(TKey,TValue);;Argument[1];ReturnValue.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | @@ -726,12 +720,6 @@ | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;GetValueOrDefault<,>;(System.Collections.Immutable.IImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[0].Element;ReturnValue;taint;generated | @@ -802,8 +790,6 @@ | System.Collections.Immutable;ImmutableDictionary<,>;false;set_Item;(System.Object,System.Object);;Argument[1];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | | System.Collections.Immutable;ImmutableDictionary<,>;false;set_Item;(TKey,TValue);;Argument[0];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Immutable;ImmutableDictionary<,>;false;set_Item;(TKey,TValue);;Argument[1];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | -| System.Collections.Immutable;ImmutableHashSet;false;Create<>;(System.Collections.Generic.IEqualityComparer,T);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableHashSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;ToImmutableHashSet<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | @@ -836,9 +822,6 @@ | System.Collections.Immutable;ImmutableHashSet<>;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated | | System.Collections.Immutable;ImmutableInterlocked;false;GetOrAdd<,>;(System.Collections.Immutable.ImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;Create<>;(T[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;Remove<>;(System.Collections.Immutable.IImmutableList,T);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;RemoveRange<>;(System.Collections.Immutable.IImmutableList,System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;Replace<>;(System.Collections.Immutable.IImmutableList,T,T);;Argument[0].Element;ReturnValue;taint;generated | @@ -945,12 +928,6 @@ | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[1];ReturnValue;taint;generated | @@ -988,7 +965,6 @@ | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_Item;(TKey,TValue);;Argument[1];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_KeyComparer;(System.Collections.Generic.IComparer);;Argument[0];Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_ValueComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Add;(System.Collections.Generic.KeyValuePair);;Argument[0].Property[System.Collections.Generic.KeyValuePair<,>.Key];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>;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 | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Add;(System.Collections.Generic.KeyValuePair);;Argument[0];Argument[Qualifier].Element;value;manual | @@ -1027,10 +1003,7 @@ | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;set_Item;(TKey,TValue);;Argument[0];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;set_Item;(TKey,TValue);;Argument[1];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | | System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[1];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T[]);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateBuilder<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated | @@ -1047,7 +1020,6 @@ | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Immutable.ImmutableSortedSet<>+Enumerator.Current];value;manual | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;IntersectWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;Reverse;();;Argument[0].Element;ReturnValue.Element;value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;SymmetricExceptWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;ToImmutable;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1068,12 +1040,8 @@ | System.Collections.Immutable;ImmutableSortedSet<>;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Immutable.ImmutableSortedSet<>+Enumerator.Current];value;manual | | System.Collections.Immutable;ImmutableSortedSet<>;false;Insert;(System.Int32,System.Object);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.Immutable;ImmutableSortedSet<>;false;Insert;(System.Int32,T);;Argument[1];Argument[Qualifier].Element;value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>;false;Intersect;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;Remove;(T);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;Reverse;();;Argument[0].Element;ReturnValue.Element;value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;ToBuilder;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1111,27 +1079,21 @@ | System.Collections.ObjectModel;Collection<>;false;Insert;(System.Int32,System.Object);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;Collection<>;false;Insert;(System.Int32,T);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;Collection<>;false;get_Item;(System.Int32);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.ObjectModel;Collection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;Collection<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;Collection<>;false;set_Item;(System.Int32,System.Object);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;Collection<>;false;set_Item;(System.Int32,T);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;KeyedCollection;(System.Collections.Generic.IEqualityComparer,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;TryGetValue;(TKey,TItem);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Dictionary;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Item;(TKey);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;Add;(System.Object);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;Add;(T);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;CopyTo;(System.Array,System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | @@ -1328,7 +1290,6 @@ | System.Collections;CollectionBase;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | | System.Collections;CollectionBase;false;Insert;(System.Int32,System.Object);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | -| System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[Qualifier];Argument[0];taint;generated | | System.Collections;CollectionBase;false;get_InnerList;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections;CollectionBase;false;get_Item;(System.Int32);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections;CollectionBase;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -1559,9 +1520,7 @@ | System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated | | System.ComponentModel;BindingList<>;false;Find;(System.ComponentModel.PropertyDescriptor,System.Object);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.ComponentModel;CategoryAttribute;false;CategoryAttribute;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.ComponentModel;CategoryAttribute;false;get_Category;();;Argument[Qualifier];ReturnValue;taint;generated | | System.ComponentModel;CharConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated | @@ -1882,10 +1841,6 @@ | System.Data.Common;DataTableMappingCollection;false;set_Item;(System.String,System.Object);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Data.Common;DbCommand;false;ExecuteReader;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;ExecuteReader;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Connection;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Parameters;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Transaction;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -1893,7 +1848,6 @@ | System.Data.Common;DbCommand;false;set_Connection;(System.Data.IDbConnection);;Argument[0];Argument[Qualifier];taint;generated | | System.Data.Common;DbCommand;false;set_Transaction;(System.Data.Common.DbTransaction);;Argument[0];Argument[Qualifier];taint;generated | | System.Data.Common;DbCommand;false;set_Transaction;(System.Data.IDbTransaction);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data.Common;DbCommand;true;ExecuteDbDataReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;true;PrepareAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1926,7 +1880,6 @@ | System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[2];Argument[0];taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;CopyTo;(System.Array,System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data.Common;DbConnectionStringBuilder;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | -| System.Data.Common;DbConnectionStringBuilder;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetProperties;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetProperties;(System.Attribute[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated | @@ -1961,13 +1914,10 @@ | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[0];ReturnValue;taint;generated | | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[1];ReturnValue;taint;generated | | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[3];ReturnValue;taint;generated | -| System.Data.Common;DbDataReader;false;GetFieldValueAsync<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | | System.Data.Common;DbDataReader;true;GetFieldValue<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbDataReader;true;GetFieldValueAsync<>;(System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetProviderSpecificValue;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetProviderSpecificValues;(System.Object[]);;Argument[Qualifier];Argument[0].Element;taint;generated | -| System.Data.Common;DbDataReader;true;GetSchemaTableAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetTextReader;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataRecord;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated | | System.Data.Common;DbEnumerator;false;DbEnumerator;(System.Data.IDataReader);;Argument[0];Argument[Qualifier];taint;generated | @@ -2108,11 +2058,8 @@ | System.Data;DataColumn;false;set_Prefix;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataColumnChangeEventArgs;false;DataColumnChangeEventArgs;(System.Data.DataRow,System.Data.DataColumn,System.Object);;Argument[1];Argument[Qualifier];taint;generated | | System.Data;DataColumnChangeEventArgs;false;get_Column;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataColumnCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataColumnCollection;false;Add;(System.Data.DataColumn);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataColumnCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier].Element;value;manual | -| System.Data;DataColumnCollection;false;Add;(System.String,System.Type);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataColumnCollection;false;Add;(System.String,System.Type,System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataColumnCollection;false;AddRange;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataColumnCollection;false;CopyTo;(System.Data.DataColumn[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataColumnCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | @@ -2120,7 +2067,6 @@ | System.Data;DataColumnCollection;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetDateTime;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetFieldValue<>;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | -| System.Data;DataReaderExtensions;false;GetFieldValueAsync<>;(System.Data.Common.DbDataReader,System.String,System.Threading.CancellationToken);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetGuid;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetProviderSpecificValue;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetString;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | @@ -2152,16 +2098,10 @@ | System.Data;DataRelationCollection;false;Add;(System.Data.DataRelation);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataRelationCollection;false;CopyTo;(System.Data.DataRelation[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataRelationCollection;false;Remove;(System.Data.DataRelation);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;AddRange;(System.Data.DataRelation[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataRow;false;DataRow;(System.Data.DataRowBuilder);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRow;false;GetChildRows;(System.Data.DataRelation);;Argument[Qualifier];ReturnValue;taint;generated | @@ -2267,12 +2207,10 @@ | System.Data;DataTable;false;set_PrimaryKey;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Data;DataTable;false;set_Site;(System.ComponentModel.ISite);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataTable;false;set_TableName;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data;DataTableCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataTableCollection;false;Add;(System.Data.DataTable);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];ReturnValue;taint;generated | -| System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataTableCollection;false;AddRange;(System.Data.DataTable[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;CopyTo;(System.Data.DataTable[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataTableCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | @@ -2867,13 +2805,10 @@ | System.IO.Compression;BrotliStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Compression;BrotliStream;false;BrotliStream;(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO.Compression;BrotliStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO.Compression;BrotliStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;BrotliStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Compression;BrotliStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO.Compression;BrotliStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;BrotliStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Compression;BrotliStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO.Compression;BrotliStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;BrotliStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;DeflateStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Compression;DeflateStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | @@ -2884,28 +2819,22 @@ | System.IO.Compression;DeflateStream;false;DeflateStream;(System.IO.Stream,System.IO.Compression.CompressionMode);;Argument[0];ReturnValue;taint;manual | | System.IO.Compression;DeflateStream;false;DeflateStream;(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean);;Argument[0];ReturnValue;taint;manual | | System.IO.Compression;DeflateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO.Compression;DeflateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;DeflateStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Compression;DeflateStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO.Compression;DeflateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;DeflateStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Compression;DeflateStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO.Compression;DeflateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;DeflateStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;GZipStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Compression;GZipStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Compression;GZipStream;false;CopyTo;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO.Compression;GZipStream;false;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | | System.IO.Compression;GZipStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO.Compression;GZipStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;GZipStream;false;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionLevel,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO.Compression;GZipStream;false;GZipStream;(System.IO.Stream,System.IO.Compression.CompressionMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO.Compression;GZipStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Compression;GZipStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO.Compression;GZipStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;GZipStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Compression;GZipStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO.Compression;GZipStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;GZipStream;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Compression;ZipArchive;false;CreateEntry;(System.String);;Argument[0];ReturnValue;taint;generated | | System.IO.Compression;ZipArchive;false;CreateEntry;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | @@ -2939,7 +2868,6 @@ | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | @@ -2965,14 +2893,11 @@ | System.IO.Pipes;NamedPipeServerStream;false;WaitForConnectionAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO.Pipes;PipeStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Pipes;PipeStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO.Pipes;PipeStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Pipes;PipeStream;false;InitializeHandle;(Microsoft.Win32.SafeHandles.SafePipeHandle,System.Boolean,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO.Pipes;PipeStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO.Pipes;PipeStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO.Pipes;PipeStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Pipes;PipeStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO.Pipes;PipeStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO.Pipes;PipeStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.Pipes;PipeStream;false;get_SafePipeHandle;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BinaryReader;false;BinaryReader;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;BinaryReader;false;BinaryReader;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | @@ -2982,7 +2907,6 @@ | System.IO;BinaryReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | -| System.IO;BinaryWriter;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BinaryWriter;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -2991,13 +2915,10 @@ | System.IO;BufferedStream;false;BufferedStream;(System.IO.Stream,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;BufferedStream;false;CopyTo;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;BufferedStream;false;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | -| System.IO;BufferedStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BufferedStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;BufferedStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO;BufferedStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BufferedStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;BufferedStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO;BufferedStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BufferedStream;false;get_UnderlyingStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Directory;false;CreateDirectory;(System.String);;Argument[0];ReturnValue;taint;generated | | System.IO;Directory;false;GetParent;(System.String);;Argument[0];ReturnValue;taint;generated | @@ -3057,7 +2978,6 @@ | System.IO;FileStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;FileStream;false;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;FileStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;FileStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | @@ -3091,7 +3011,6 @@ | System.IO;MemoryStream;false;CopyTo;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;MemoryStream;false;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;MemoryStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO;MemoryStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;MemoryStream;false;GetBuffer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;MemoryStream;false;MemoryStream;(System.Byte[]);;Argument[0];ReturnValue;taint;manual | | System.IO;MemoryStream;false;MemoryStream;(System.Byte[],System.Boolean);;Argument[0].Element;ReturnValue;taint;manual | @@ -3100,12 +3019,10 @@ | System.IO;MemoryStream;false;MemoryStream;(System.Byte[],System.Int32,System.Int32,System.Boolean,System.Boolean);;Argument[0].Element;ReturnValue;taint;manual | | System.IO;MemoryStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;MemoryStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO;MemoryStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;MemoryStream;false;ToArray;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;MemoryStream;false;TryGetBuffer;(System.ArraySegment);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;MemoryStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;MemoryStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO;MemoryStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;MemoryStream;false;WriteTo;(System.IO.Stream);;Argument[Qualifier];Argument[0];taint;generated | | System.IO;Path;false;ChangeExtension;(System.String,System.String);;Argument[0];ReturnValue;taint;generated | | System.IO;Path;false;Combine;(System.String,System.String);;Argument[0];ReturnValue;taint;manual | @@ -3150,7 +3067,6 @@ | System.IO;Stream;false;CopyToAsync;(System.IO.Stream);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;false;CopyToAsync;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;false;CopyToAsync;(System.IO.Stream,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | -| System.IO;Stream;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;Stream;false;Synchronized;(System.IO.Stream);;Argument[0];ReturnValue;taint;generated | | System.IO;Stream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | @@ -3158,13 +3074,10 @@ | System.IO;Stream;true;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;Stream;true;CopyTo;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;true;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | -| System.IO;Stream;true;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;true;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;Stream;true;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO;Stream;true;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;true;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;Stream;true;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO;Stream;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamReader;false;Read;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;StreamReader;false;Read;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;StreamReader;false;Read;(System.Span);;Argument[Qualifier];ReturnValue;taint;manual | @@ -3182,7 +3095,6 @@ | System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | | System.IO;StreamReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamReader;false;get_CurrentEncoding;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamWriter;false;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;StreamWriter;(System.IO.Stream,System.Text.Encoding,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;Write;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | @@ -3197,14 +3109,7 @@ | System.IO;StreamWriter;false;Write;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;Write;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;Write;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamWriter;false;WriteLine;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;WriteLine;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;WriteLine;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated | @@ -3217,15 +3122,7 @@ | System.IO;StreamWriter;false;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamWriter;false;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;StreamWriter;false;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamWriter;false;get_Encoding;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringReader;false;Read;();;Argument[Qualifier];ReturnValue;taint;manual | @@ -3241,39 +3138,19 @@ | System.IO;StringReader;false;ReadToEnd;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;StringReader;false;ReadToEndAsync;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;StringReader;false;StringReader;(System.String);;Argument[0];ReturnValue;taint;manual | -| System.IO;StringWriter;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;GetStringBuilder;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;StringWriter;(System.Text.StringBuilder,System.IFormatProvider);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;Write;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;Write;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;Write;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextReader;false;Synchronized;(System.IO.TextReader);;Argument[0];ReturnValue;taint;generated | | System.IO;TextReader;true;Read;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;TextReader;true;Read;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;manual | @@ -3291,12 +3168,7 @@ | System.IO;TextWriter;false;Synchronized;(System.IO.TextWriter);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;false;TextWriter;(System.IFormatProvider);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;Write;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated | @@ -3310,14 +3182,7 @@ | System.IO;TextWriter;true;Write;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLine;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | @@ -3334,18 +3199,8 @@ | System.IO;TextWriter;true;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;get_FormatProvider;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;get_NewLine;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;set_NewLine;(System.String);;Argument[0];Argument[Qualifier];taint;generated | @@ -3353,19 +3208,16 @@ | System.IO;UnmanagedMemoryAccessor;false;UnmanagedMemoryAccessor;(System.Runtime.InteropServices.SafeBuffer,System.Int64,System.Int64);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryAccessor;false;UnmanagedMemoryAccessor;(System.Runtime.InteropServices.SafeBuffer,System.Int64,System.Int64,System.IO.FileAccess);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.IO;UnmanagedMemoryStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;UnmanagedMemoryStream;false;Initialize;(System.Byte*,System.Int64,System.Int64,System.IO.FileAccess);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;Initialize;(System.Runtime.InteropServices.SafeBuffer,System.Int64,System.Int64,System.IO.FileAccess);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;UnmanagedMemoryStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO;UnmanagedMemoryStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;UnmanagedMemoryStream;false;UnmanagedMemoryStream;(System.Byte*,System.Int64);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;UnmanagedMemoryStream;(System.Byte*,System.Int64,System.Int64,System.IO.FileAccess);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;UnmanagedMemoryStream;(System.Runtime.InteropServices.SafeBuffer,System.Int64,System.Int64);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;UnmanagedMemoryStream;(System.Runtime.InteropServices.SafeBuffer,System.Int64,System.Int64,System.IO.FileAccess);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;UnmanagedMemoryStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;UnmanagedMemoryStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO;UnmanagedMemoryStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;UnmanagedMemoryStream;false;get_PositionPointer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Linq.Expressions;BinaryExpression;false;Accept;(System.Linq.Expressions.ExpressionVisitor);;Argument[Qualifier];Argument[0];taint;generated | | System.Linq.Expressions;BinaryExpression;false;Accept;(System.Linq.Expressions.ExpressionVisitor);;Argument[Qualifier];ReturnValue;taint;generated | @@ -4687,29 +4539,18 @@ | System.Net.Http.Headers;WarningHeaderValue;false;get_Text;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http.Json;JsonContent;false;Create;(System.Object,System.Type,System.Net.Http.Headers.MediaTypeHeaderValue,System.Text.Json.JsonSerializerOptions);;Argument[3];ReturnValue;taint;generated | | System.Net.Http.Json;JsonContent;false;Create<>;(T,System.Net.Http.Headers.MediaTypeHeaderValue,System.Text.Json.JsonSerializerOptions);;Argument[2];ReturnValue;taint;generated | -| System.Net.Http.Json;JsonContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http.Json;JsonContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http.Json;JsonContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Http;ByteArrayContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;ByteArrayContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;DelegatingHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;DelegatingHandler;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;get_InnerHandler;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;set_InnerHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | @@ -4736,10 +4577,7 @@ | System.Net.Http;HttpContent;false;ReadAsStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpContent;false;get_Headers;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpContent;true;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;HttpContent;true;CreateContentReadStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpMessageInvoker;false;HttpMessageInvoker;(System.Net.Http.HttpMessageHandler,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;HttpMessageInvoker;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Http;HttpMethod;false;HttpMethod;(System.String);;Argument[0];Argument[Qualifier];taint;generated | @@ -4781,27 +4619,19 @@ | System.Net.Http;MessageProcessingHandler;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Http;MultipartContent;false;Add;(System.Net.Http.HttpContent);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Net.Http;MultipartContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;MultipartContent;false;CreateContentReadStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;MultipartContent;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Generic.IEnumerator<>.Current];value;manual | | System.Net.Http;MultipartContent;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | | System.Net.Http;MultipartContent;false;MultipartContent;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Net.Http;MultipartContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Http;MultipartContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;MultipartContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;MultipartContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;MultipartContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;MultipartFormDataContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;MultipartFormDataContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;MultipartFormDataContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ReadOnlyMemoryContent;false;ReadOnlyMemoryContent;(System.ReadOnlyMemory);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;ReadOnlyMemoryContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;ReadOnlyMemoryContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ReadOnlyMemoryContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;SocketsHttpConnectionContext;false;get_DnsEndPoint;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;SocketsHttpConnectionContext;false;get_InitialRequestMessage;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;SocketsHttpHandler;false;get_ConnectCallback;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4838,19 +4668,11 @@ | System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_PlaintextStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[0];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[1];ReturnValue;taint;generated | @@ -4996,7 +4818,6 @@ | System.Net.NetworkInformation;UnicastIPAddressInformationCollection;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | | System.Net.NetworkInformation;UnicastIPAddressInformationCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;AuthenticatedStream;false;AuthenticatedStream;(System.IO.Stream,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Security;AuthenticatedStream;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;AuthenticatedStream;false;get_InnerStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[2];Argument[Qualifier];taint;generated | @@ -5017,7 +4838,6 @@ | System.Net.Security;NegotiateStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Security;NegotiateStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Security;NegotiateStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | @@ -5034,14 +4854,11 @@ | System.Net.Security;SslStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Security;SslStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.Net.Security;SslStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Net.Security;SslStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Security;SslStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.Net.Security;SslStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslStream;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Security;SslStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.Net.Security;SslStream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.Net.Security;SslStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslStream;false;get_LocalCertificate;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslStream;false;get_NegotiatedApplicationProtocol;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslStream;false;get_RemoteCertificate;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -5062,7 +4879,6 @@ | System.Net.Sockets;MulticastOption;false;set_LocalAddress;(System.Net.IPAddress);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;NetworkStream;false;BeginRead;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Sockets;NetworkStream;false;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.Net.Sockets;NetworkStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;NetworkStream;false;NetworkStream;(System.Net.Sockets.Socket,System.IO.FileAccess,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;NetworkStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Net.Sockets;NetworkStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | @@ -5084,7 +4900,6 @@ | System.Net.Sockets;Socket;false;ConnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;Socket;false;ConnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Sockets;Socket;false;DisconnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Sockets;Socket;false;EndAccept;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;Socket;false;ReceiveAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Sockets;Socket;false;ReceiveFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[4];Argument[Qualifier];taint;generated | | System.Net.Sockets;Socket;false;ReceiveFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[4];ReturnValue;taint;generated | @@ -5127,7 +4942,6 @@ | System.Net.Sockets;SocketAsyncEventArgs;false;set_SendPacketsElements;(System.Net.Sockets.SendPacketsElement[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Sockets;SocketAsyncEventArgs;false;set_UserToken;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;SocketException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;AcceptAsync;(System.Net.Sockets.Socket,System.Net.Sockets.Socket);;Argument[1];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint);;Argument[1];Argument[0];taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated | @@ -5139,8 +4953,6 @@ | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;ReceiveFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;ReceiveMessageFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendToAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];Argument[0];taint;generated | @@ -5151,8 +4963,6 @@ | System.Net.Sockets;TcpClient;false;set_Client;(System.Net.Sockets.Socket);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;AcceptSocket;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;TcpListener;false;AcceptTcpClient;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Sockets;TcpListener;false;EndAcceptSocket;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net.Sockets;TcpListener;false;EndAcceptTcpClient;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPAddress,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPEndPoint);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;get_LocalEndpoint;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -5230,17 +5040,11 @@ | System.Net;CookieException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated | | System.Net;CredentialCache;false;GetCredential;(System.Uri,System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;CredentialCache;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | -| System.Net;Dns;false;EndGetHostAddresses;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndGetHostByName;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndGetHostEntry;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndResolve;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;DnsEndPoint;false;DnsEndPoint;(System.String,System.Int32,System.Net.Sockets.AddressFamily);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;DnsEndPoint;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DnsEndPoint;false;get_Host;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DownloadDataCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DownloadStringCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net;FileWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;FileWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;get_ContentType;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -5319,9 +5123,6 @@ | System.Net;HttpListenerTimeoutManager;false;get_IdleConnection;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpListenerTimeoutManager;false;set_DrainEntityBody;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;HttpListenerTimeoutManager;false;set_IdleConnection;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | -| System.Net;HttpWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetRequestStream;(System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -5407,8 +5208,6 @@ | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest);;Argument[0];ReturnValue;taint;generated | | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];Argument[Qualifier];taint;generated | -| System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];ReturnValue;taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | @@ -6342,7 +6141,6 @@ | System.Resources;ResourceReader;false;GetResourceData;(System.String,System.String,System.Byte[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Resources;ResourceReader;false;ResourceReader;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | | System.Resources;ResourceSet;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.IEnumerator.Current];value;manual | -| System.Resources;ResourceSet;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Resources;ResourceSet;false;ResourceSet;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | | System.Resources;ResourceSet;false;ResourceSet;(System.Resources.IResourceReader);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Resources;ResourceWriter;false;ResourceWriter;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | @@ -6725,7 +6523,6 @@ | System.Security.Cryptography.X509Certificates;X509Certificate;false;ToString;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Issuer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Subject;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;X509CertificateEnumerator;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;Add;(System.Security.Cryptography.X509Certificates.X509Certificate);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;AddRange;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier].Element;value;manual | @@ -6794,7 +6591,6 @@ | System.Security.Cryptography;CryptoStream;false;CryptoStream;(System.IO.Stream,System.Security.Cryptography.ICryptoTransform,System.Security.Cryptography.CryptoStreamMode,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Security.Cryptography;CryptoStream;false;CryptoStream;(System.IO.Stream,System.Security.Cryptography.ICryptoTransform,System.Security.Cryptography.CryptoStreamMode,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | | System.Security.Cryptography;CryptoStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Security.Cryptography;CryptoStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography;CryptoStream;false;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Security.Cryptography;CryptoStream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.Security.Cryptography;CryptoStream;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | @@ -7313,10 +7109,6 @@ | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;SendAsync<>;(System.Threading.Tasks.Dataflow.ITargetBlock,TInput,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;TryReceive<>;(System.Threading.Tasks.Dataflow.IReceivableSourceBlock,TOutput);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlockOptions;false;get_CancellationToken;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -8103,12 +7895,9 @@ | System.Xml.Schema;XmlSchemaSet;false;Add;(System.String,System.Xml.XmlReader);;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchemaSet);;Argument[0];Argument[Qualifier];taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;CopyTo;(System.Xml.Schema.XmlSchema[],System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Remove;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;Schemas;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;XmlSchemaSet;(System.Xml.XmlNameTable);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;get_CompilationSettings;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;get_GlobalAttributes;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -8195,10 +7984,8 @@ | System.Xml.Schema;XmlSchemaXPath;false;get_XPath;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaXPath;false;set_XPath;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[0];ReturnValue;taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;MakeUnique;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;ToArray;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Serialization;ImportContext;false;ImportContext;(System.Xml.Serialization.CodeIdentifiers,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | @@ -8505,7 +8292,6 @@ | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.Xml.XmlQualifiedName);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteId;(System.Object);;Argument[Qualifier];Argument[0];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncoded;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.Byte[],System.Xml.XmlQualifiedName);;Argument[2].Element;Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | @@ -8513,15 +8299,9 @@ | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.Byte[]);;Argument[2].Element;Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteSerializable;(System.Xml.Serialization.IXmlSerializable,System.String,System.String,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | @@ -10357,27 +10137,6 @@ | System;TupleExtensions;false;Deconstruct<,>;(System.Tuple,T1,T2);;Argument[0].Property[System.Tuple<,>.Item1];Argument[1];value;manual | | System;TupleExtensions;false;Deconstruct<,>;(System.Tuple,T1,T2);;Argument[0].Property[System.Tuple<,>.Item2];Argument[2];value;manual | | System;TupleExtensions;false;Deconstruct<>;(System.Tuple,T1);;Argument[0].Property[System.Tuple<>.Item1];Argument[1];value;manual | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | diff --git a/csharp/ql/test/library-tests/dataflow/library/FlowSummariesFiltered.expected b/csharp/ql/test/library-tests/dataflow/library/FlowSummariesFiltered.expected index 1a2f70f48e8..a77b56ab64b 100644 --- a/csharp/ql/test/library-tests/dataflow/library/FlowSummariesFiltered.expected +++ b/csharp/ql/test/library-tests/dataflow/library/FlowSummariesFiltered.expected @@ -255,7 +255,6 @@ | System.Collections.Concurrent;ConcurrentDictionary<,>;false;ConcurrentDictionary;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[0].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;ConcurrentDictionary;(System.Int32,System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[1].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];ReturnValue.Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;ConcurrentDictionary;(System.Int32,System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[1].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | -| System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;GetOrAdd;(TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Keys;();;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];ReturnValue.Element;value;manual | | System.Collections.Concurrent;ConcurrentDictionary<,>;false;get_Values;();;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element;value;manual | @@ -275,9 +274,6 @@ | System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Generic;CollectionExtensions;false;GetValueOrDefault<,>;(System.Collections.Generic.IReadOnlyDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | | System.Collections.Generic;CollectionExtensions;false;Remove<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[0].Element;Argument[2];taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[1];Argument[0].Element;taint;generated | -| System.Collections.Generic;CollectionExtensions;false;TryAdd<,>;(System.Collections.Generic.IDictionary,TKey,TValue);;Argument[2];Argument[0].Element;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Entry;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;Dictionary<,>+Enumerator;false;get_Key;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -327,8 +323,6 @@ | System.Collections.Generic;IList<>;true;get_Item;(System.Int32);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.Generic;IList<>;true;set_Item;(System.Int32,T);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Collections.Generic;ISet<>;true;Add;(T);;Argument[0];Argument[Qualifier].Element;value;manual | -| System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Generic;KeyValuePair;false;Create<,>;(TKey,TValue);;Argument[1];ReturnValue;taint;generated | | System.Collections.Generic;KeyValuePair<,>;false;Deconstruct;(TKey,TValue);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Generic;KeyValuePair<,>;false;KeyValuePair;(TKey,TValue);;Argument[0];ReturnValue.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Generic;KeyValuePair<,>;false;KeyValuePair;(TKey,TValue);;Argument[1];ReturnValue.Property[System.Collections.Generic.KeyValuePair<,>.Value];value;manual | @@ -518,12 +512,6 @@ | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;Create<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableDictionary;false;CreateRange<,>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;GetValueOrDefault<,>;(System.Collections.Immutable.IImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary;false;ToImmutableDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IEqualityComparer);;Argument[0].Element;ReturnValue;taint;generated | @@ -567,8 +555,6 @@ | System.Collections.Immutable;ImmutableDictionary<,>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated | | System.Collections.Immutable;ImmutableDictionary<,>;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableDictionary<,>;false;get_Values;();;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element;value;manual | -| System.Collections.Immutable;ImmutableHashSet;false;Create<>;(System.Collections.Generic.IEqualityComparer,T);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableHashSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;CreateRange<>;(System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet;false;ToImmutableHashSet<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | @@ -593,9 +579,6 @@ | System.Collections.Immutable;ImmutableHashSet<>;false;get_KeyComparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableHashSet<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated | | System.Collections.Immutable;ImmutableInterlocked;false;GetOrAdd<,>;(System.Collections.Immutable.ImmutableDictionary,TKey,TValue);;Argument[2];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;Create<>;(T[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableList;false;CreateRange<>;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;Remove<>;(System.Collections.Immutable.IImmutableList,T);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;RemoveRange<>;(System.Collections.Immutable.IImmutableList,System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableList;false;Replace<>;(System.Collections.Immutable.IImmutableList,T,T);;Argument[0].Element;ReturnValue;taint;generated | @@ -681,12 +664,6 @@ | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;CreateBuilder<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable>);;Argument[1].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[1];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEqualityComparer,System.Collections.Generic.IEnumerable>);;Argument[2].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary;false;CreateRange<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[0].Element;ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary;false;ToImmutableSortedDictionary<,>;(System.Collections.Generic.IEnumerable>,System.Collections.Generic.IComparer);;Argument[1];ReturnValue;taint;generated | @@ -709,7 +686,6 @@ | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;get_Values;();;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element;value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_KeyComparer;(System.Collections.Generic.IComparer);;Argument[0];Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary<,>+Builder;false;set_ValueComparer;(System.Collections.Generic.IEqualityComparer);;Argument[0];Argument[Qualifier];taint;generated | -| System.Collections.Immutable;ImmutableSortedDictionary<,>+Enumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Add;(System.Collections.Generic.KeyValuePair);;Argument[0].Property[System.Collections.Generic.KeyValuePair<,>.Key];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | | System.Collections.Immutable;ImmutableSortedDictionary<,>;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 | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;Add;(TKey,TValue);;Argument[0];Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Key];value;manual | @@ -736,10 +712,7 @@ | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;get_ValueComparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedDictionary<,>;false;get_Values;();;Argument[Qualifier].Element.Property[System.Collections.Generic.KeyValuePair<,>.Value];ReturnValue.Element;value;manual | | System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T);;Argument[1];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(System.Collections.Generic.IComparer,T[]);;Argument[0];ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet;false;Create<>;(T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateBuilder<>;(System.Collections.Generic.IComparer);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet;false;CreateRange<>;(System.Collections.Generic.IComparer,System.Collections.Generic.IEnumerable);;Argument[1].Element;ReturnValue;taint;generated | @@ -751,7 +724,6 @@ | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Immutable.ImmutableSortedSet<>+Enumerator.Current];value;manual | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;IntersectWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;Reverse;();;Argument[0].Element;ReturnValue.Element;value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;SymmetricExceptWith;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;ToImmutable;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>+Builder;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated | @@ -765,12 +737,8 @@ | System.Collections.Immutable;ImmutableSortedSet<>;false;Add;(T);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Collections.Immutable;ImmutableSortedSet<>;false;Except;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;GetEnumerator;();;Argument[Qualifier].Element;ReturnValue.Property[System.Collections.Immutable.ImmutableSortedSet<>+Enumerator.Current];value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>;false;Intersect;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;Remove;(T);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;Reverse;();;Argument[0].Element;ReturnValue.Element;value;manual | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[0].Element;ReturnValue;taint;generated | -| System.Collections.Immutable;ImmutableSortedSet<>;false;SymmetricExcept;(System.Collections.Generic.IEnumerable);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;ToBuilder;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[0];ReturnValue;taint;generated | | System.Collections.Immutable;ImmutableSortedSet<>;false;TryGetValue;(T,T);;Argument[Qualifier];ReturnValue;taint;generated | @@ -796,24 +764,18 @@ | System.Collections.Immutable;ImmutableStack<>;false;Push;(T);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;Collection<>;false;Collection;(System.Collections.Generic.IList);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;Collection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;Collection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;Collection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;Collection<>;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;KeyedCollection<,>;false;InsertItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;KeyedCollection;(System.Collections.Generic.IEqualityComparer,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;KeyedCollection<,>;false;SetItem;(System.Int32,TItem);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;TryGetValue;(TKey,TItem);;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Comparer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Dictionary;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections.ObjectModel;KeyedCollection<,>;false;get_Item;(TKey);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;ObservableCollection<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.Collections.ObjectModel;ObservableCollection<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;ReadOnlyCollection;(System.Collections.Generic.IList);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;get_Item;(System.Int32);;Argument[Qualifier].Element;ReturnValue;value;manual | | System.Collections.ObjectModel;ReadOnlyCollection<>;false;get_Items;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -924,7 +886,6 @@ | System.Collections;BitArray;false;Xor;(System.Collections.BitArray);;Argument[Qualifier];ReturnValue;value;generated | | System.Collections;BitArray;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated | | System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | -| System.Collections;CollectionBase;false;Remove;(System.Object);;Argument[Qualifier];Argument[0];taint;generated | | System.Collections;CollectionBase;false;get_InnerList;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections;CollectionBase;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Collections;CollectionBase;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -1101,9 +1062,7 @@ | System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[1];ReturnValue;taint;generated | | System.ComponentModel;BaseNumberConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated | | System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.ComponentModel;BindingList<>;false;InsertItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[1];Argument[Qualifier];taint;generated | -| System.ComponentModel;BindingList<>;false;SetItem;(System.Int32,T);;Argument[Qualifier];Argument[1];taint;generated | | System.ComponentModel;CategoryAttribute;false;CategoryAttribute;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.ComponentModel;CategoryAttribute;false;get_Category;();;Argument[Qualifier];ReturnValue;taint;generated | | System.ComponentModel;CharConverter;false;ConvertTo;(System.ComponentModel.ITypeDescriptorContext,System.Globalization.CultureInfo,System.Object,System.Type);;Argument[2];ReturnValue;taint;generated | @@ -1388,10 +1347,6 @@ | System.Data.Common;DataTableMappingCollection;false;set_Item;(System.String,System.Data.Common.DataTableMapping);;Argument[1];Argument[Qualifier].Element;value;manual | | System.Data.Common;DbCommand;false;ExecuteReader;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;ExecuteReader;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbCommand;false;ExecuteReaderAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Connection;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Parameters;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;false;get_Transaction;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -1399,7 +1354,6 @@ | System.Data.Common;DbCommand;false;set_Connection;(System.Data.IDbConnection);;Argument[0];Argument[Qualifier];taint;generated | | System.Data.Common;DbCommand;false;set_Transaction;(System.Data.Common.DbTransaction);;Argument[0];Argument[Qualifier];taint;generated | | System.Data.Common;DbCommand;false;set_Transaction;(System.Data.IDbTransaction);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data.Common;DbCommand;true;ExecuteDbDataReaderAsync;(System.Data.CommandBehavior,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommand;true;PrepareAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbCommandBuilder;false;GetDeleteCommand;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1428,7 +1382,6 @@ | System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String);;Argument[2];Argument[0];taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[1];Argument[0];taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;AppendKeyValuePair;(System.Text.StringBuilder,System.String,System.String,System.Boolean);;Argument[2];Argument[0];taint;generated | -| System.Data.Common;DbConnectionStringBuilder;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetProperties;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetProperties;(System.Attribute[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbConnectionStringBuilder;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated | @@ -1458,12 +1411,9 @@ | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[0];ReturnValue;taint;generated | | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[1];ReturnValue;taint;generated | | System.Data.Common;DbDataAdapter;true;CreateRowUpdatingEvent;(System.Data.DataRow,System.Data.IDbCommand,System.Data.StatementType,System.Data.Common.DataTableMapping);;Argument[3];ReturnValue;taint;generated | -| System.Data.Common;DbDataReader;false;GetFieldValueAsync<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetFieldValue<>;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data.Common;DbDataReader;true;GetFieldValueAsync<>;(System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetProviderSpecificValue;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetProviderSpecificValues;(System.Object[]);;Argument[Qualifier];Argument[0].Element;taint;generated | -| System.Data.Common;DbDataReader;true;GetSchemaTableAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataReader;true;GetTextReader;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data.Common;DbDataRecord;false;GetPropertyOwner;(System.ComponentModel.PropertyDescriptor);;Argument[Qualifier];ReturnValue;value;generated | | System.Data.Common;DbEnumerator;false;DbEnumerator;(System.Data.IDataReader);;Argument[0];Argument[Qualifier];taint;generated | @@ -1598,11 +1548,8 @@ | System.Data;DataColumn;false;set_Prefix;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataColumnChangeEventArgs;false;DataColumnChangeEventArgs;(System.Data.DataRow,System.Data.DataColumn,System.Object);;Argument[1];Argument[Qualifier];taint;generated | | System.Data;DataColumnChangeEventArgs;false;get_Column;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataColumnCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataColumnCollection;false;Add;(System.Data.DataColumn);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataColumnCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier].Element;value;manual | -| System.Data;DataColumnCollection;false;Add;(System.String,System.Type);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataColumnCollection;false;Add;(System.String,System.Type,System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataColumnCollection;false;AddRange;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataColumnCollection;false;CopyTo;(System.Data.DataColumn[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataColumnCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1610,7 +1557,6 @@ | System.Data;DataColumnCollection;false;get_List;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetDateTime;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetFieldValue<>;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | -| System.Data;DataReaderExtensions;false;GetFieldValueAsync<>;(System.Data.Common.DbDataReader,System.String,System.Threading.CancellationToken);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetGuid;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetProviderSpecificValue;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | | System.Data;DataReaderExtensions;false;GetString;(System.Data.Common.DbDataReader,System.String);;Argument[0].Element;ReturnValue;taint;generated | @@ -1642,16 +1588,10 @@ | System.Data;DataRelationCollection;false;Add;(System.Data.DataRelation);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataRelationCollection;false;CopyTo;(System.Data.DataRelation[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataRelationCollection;false;Remove;(System.Data.DataRelation);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[0];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn,System.Data.DataColumn,System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[0];ReturnValue;taint;generated | -| System.Data;DataRelationCollection;true;Add;(System.String,System.Data.DataColumn[],System.Data.DataColumn[],System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataRelationCollection;true;AddRange;(System.Data.DataRelation[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataRow;false;DataRow;(System.Data.DataRowBuilder);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataRow;false;GetChildRows;(System.Data.DataRelation);;Argument[Qualifier];ReturnValue;taint;generated | @@ -1755,12 +1695,10 @@ | System.Data;DataTable;false;set_PrimaryKey;(System.Data.DataColumn[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Data;DataTable;false;set_Site;(System.ComponentModel.ISite);;Argument[0];Argument[Qualifier];taint;generated | | System.Data;DataTable;false;set_TableName;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.Data;DataTableCollection;false;Add;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataTableCollection;false;Add;(System.Data.DataTable);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;Add;(System.String);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[1];ReturnValue;taint;generated | -| System.Data;DataTableCollection;false;Add;(System.String,System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.Data;DataTableCollection;false;AddRange;(System.Data.DataTable[]);;Argument[0].Element;Argument[Qualifier].Element;value;manual | | System.Data;DataTableCollection;false;CopyTo;(System.Data.DataTable[],System.Int32);;Argument[Qualifier].Element;Argument[0].Element;value;manual | | System.Data;DataTableCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | @@ -2341,8 +2279,10 @@ | System.IO.IsolatedStorage;IsolatedStorage;false;get_DomainIdentity;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.IO.IsolatedStorage;IsolatedStorageFileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO.IsolatedStorage;IsolatedStorageFileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.IO.IsolatedStorage;IsolatedStorageFileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.MemoryMappedFiles;MemoryMappedFile;false;CreateFromFile;(System.IO.FileStream,System.String,System.Int64,System.IO.MemoryMappedFiles.MemoryMappedFileAccess,System.IO.HandleInheritability,System.Boolean);;Argument[0];ReturnValue;taint;generated | | System.IO.MemoryMappedFiles;MemoryMappedFile;false;get_SafeMemoryMappedFileHandle;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO.MemoryMappedFiles;MemoryMappedViewAccessor;false;get_SafeMemoryMappedViewHandle;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -2367,7 +2307,6 @@ | System.IO;BinaryReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;BinaryWriter;(System.IO.Stream,System.Text.Encoding,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | -| System.IO;BinaryWriter;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;BinaryWriter;false;Write;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;BinaryWriter;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -2427,8 +2366,10 @@ | System.IO;FileNotFoundException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;FileStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.IO;FileStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;FileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;FileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.IO;FileStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;FileStream;false;get_SafeFileHandle;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;FileSystemEventArgs;false;FileSystemEventArgs;(System.IO.WatcherChangeTypes,System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.IO;FileSystemEventArgs;false;FileSystemEventArgs;(System.IO.WatcherChangeTypes,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated | @@ -2501,7 +2442,6 @@ | System.IO;Stream;false;CopyToAsync;(System.IO.Stream);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;false;CopyToAsync;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;false;CopyToAsync;(System.IO.Stream,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | -| System.IO;Stream;false;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;false;ReadAsync;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;Stream;false;Synchronized;(System.IO.Stream);;Argument[0];ReturnValue;taint;generated | | System.IO;Stream;false;WriteAsync;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | @@ -2509,13 +2449,10 @@ | System.IO;Stream;true;BeginWrite;(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;Stream;true;CopyTo;(System.IO.Stream,System.Int32);;Argument[Qualifier];Argument[0];taint;manual | | System.IO;Stream;true;CopyToAsync;(System.IO.Stream,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;manual | -| System.IO;Stream;true;FlushAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;true;Read;(System.Byte[],System.Int32,System.Int32);;Argument[Qualifier];Argument[0].Element;taint;manual | | System.IO;Stream;true;ReadAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0].Element;taint;manual | -| System.IO;Stream;true;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;Stream;true;Write;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;manual | | System.IO;Stream;true;WriteAsync;(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken);;Argument[0].Element;Argument[Qualifier];taint;manual | -| System.IO;Stream;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StreamReader;false;StreamReader;(System.IO.Stream,System.Text.Encoding,System.Boolean,System.Int32,System.Boolean);;Argument[1];Argument[Qualifier];taint;generated | | System.IO;StreamReader;false;get_BaseStream;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -2530,13 +2467,10 @@ | System.IO;StringWriter;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;StringWriter;false;Write;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;Write;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;Write;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;StringWriter;false;WriteLineAsync;(System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;StringWriter;false;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextReader;false;Synchronized;(System.IO.TextReader);;Argument[0];ReturnValue;taint;generated | | System.IO;TextReader;true;Read;();;Argument[Qualifier];ReturnValue;taint;manual | | System.IO;TextReader;true;Read;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;manual | @@ -2554,12 +2488,7 @@ | System.IO;TextWriter;false;Synchronized;(System.IO.TextWriter);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;false;TextWriter;(System.IFormatProvider);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;false;WriteAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;false;WriteLineAsync;(System.Char[]);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;FlushAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;Write;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object);;Argument[0];Argument[Qualifier];taint;generated | @@ -2573,14 +2502,7 @@ | System.IO;TextWriter;true;Write;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;Write;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLine;(System.Char[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | @@ -2597,18 +2519,8 @@ | System.IO;TextWriter;true;WriteLine;(System.String,System.Object,System.Object,System.Object);;Argument[3];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[0];Argument[Qualifier];taint;generated | | System.IO;TextWriter;true;WriteLine;(System.String,System.Object[]);;Argument[1].Element;Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteLine;(System.Text.StringBuilder);;Argument[0];Argument[Qualifier];taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[0].Element;ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Char[],System.Int32,System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[0];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | -| System.IO;TextWriter;true;WriteLineAsync;(System.Text.StringBuilder,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;get_FormatProvider;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;get_NewLine;();;Argument[Qualifier];ReturnValue;taint;generated | | System.IO;TextWriter;true;set_NewLine;(System.String);;Argument[0];Argument[Qualifier];taint;generated | @@ -3879,17 +3791,12 @@ | System.Net.Http.Json;JsonContent;false;Create<>;(T,System.Net.Http.Headers.MediaTypeHeaderValue,System.Text.Json.JsonSerializerOptions);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Http;ByteArrayContent;false;ByteArrayContent;(System.Byte[],System.Int32,System.Int32);;Argument[0].Element;Argument[Qualifier];taint;generated | -| System.Net.Http;ByteArrayContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;ByteArrayContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;DelegatingHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;DelegatingHandler;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;get_InnerHandler;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;DelegatingHandler;false;set_InnerHandler;(System.Net.Http.HttpMessageHandler);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;FormUrlEncodedContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Http;HttpClient;false;Send;(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpCompletionOption,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | @@ -3916,10 +3823,7 @@ | System.Net.Http;HttpContent;false;ReadAsStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpContent;false;get_Headers;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpContent;true;CreateContentReadStream;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;HttpContent;true;CreateContentReadStreamAsync;(System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;HttpContent;true;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;HttpMessageInvoker;false;HttpMessageInvoker;(System.Net.Http.HttpMessageHandler,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;HttpMessageInvoker;false;SendAsync;(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Http;HttpMethod;false;HttpMethod;(System.String);;Argument[0];Argument[Qualifier];taint;generated | @@ -3955,7 +3859,6 @@ | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;MultipartFormDataContent;false;Add;(System.Net.Http.HttpContent,System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;ReadOnlyMemoryContent;false;CreateContentReadStreamAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;ReadOnlyMemoryContent;false;ReadOnlyMemoryContent;(System.ReadOnlyMemory);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;SocketsHttpConnectionContext;false;get_DnsEndPoint;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;SocketsHttpConnectionContext;false;get_InitialRequestMessage;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -3992,13 +3895,9 @@ | System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_NegotiatedHttpVersion;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;SocketsHttpPlaintextStreamFilterContext;false;get_PlaintextStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;SerializeToStream;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Http;StreamContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Http;StreamContent;false;StreamContent;(System.IO.Stream,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Http;StringContent;false;SerializeToStreamAsync;(System.IO.Stream,System.Net.TransportContext,System.Threading.CancellationToken);;Argument[2];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[0];ReturnValue;taint;generated | | System.Net.Mail;AlternateView;false;CreateAlternateViewFromString;(System.String,System.Net.Mime.ContentType);;Argument[1];ReturnValue;taint;generated | @@ -4124,7 +4023,6 @@ | System.Net.NetworkInformation;PhysicalAddress;false;PhysicalAddress;(System.Byte[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.NetworkInformation;UnicastIPAddressInformationCollection;false;get_Item;(System.Int32);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;AuthenticatedStream;false;AuthenticatedStream;(System.IO.Stream,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net.Security;AuthenticatedStream;false;DisposeAsync;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;AuthenticatedStream;false;get_InnerStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Net.Security;NegotiateStream;false;AuthenticateAsClient;(System.Net.NetworkCredential,System.Security.Authentication.ExtendedProtection.ChannelBinding,System.String);;Argument[2];Argument[Qualifier];taint;generated | @@ -4145,8 +4043,10 @@ | System.Net.Security;NegotiateStream;false;FlushAsync;(System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.Net.Security;NegotiateStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.Net.Security;NegotiateStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;NegotiateStream;false;get_RemoteIdentity;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslApplicationProtocol;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Security;SslApplicationProtocol;false;get_Protocol;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4173,7 +4073,9 @@ | System.Net.Sockets;NetworkStream;false;NetworkStream;(System.Net.Sockets.Socket,System.IO.FileAccess,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;NetworkStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;NetworkStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.Net.Sockets;NetworkStream;false;ReadAsync;(System.Memory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;NetworkStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | +| System.Net.Sockets;NetworkStream;false;WriteAsync;(System.ReadOnlyMemory,System.Threading.CancellationToken);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;NetworkStream;false;get_Socket;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;SafeSocketHandle;false;SafeSocketHandle;(System.IntPtr,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;Socket;false;Accept;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4185,7 +4087,6 @@ | System.Net.Sockets;Socket;false;ConnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;Socket;false;ConnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Sockets;Socket;false;DisconnectAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | -| System.Net.Sockets;Socket;false;EndAccept;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;Socket;false;ReceiveAsync;(System.Net.Sockets.SocketAsyncEventArgs);;Argument[Qualifier];Argument[0];taint;generated | | System.Net.Sockets;Socket;false;ReceiveFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[4];Argument[Qualifier];taint;generated | | System.Net.Sockets;Socket;false;ReceiveFrom;(System.Byte[],System.Int32,System.Int32,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[4];ReturnValue;taint;generated | @@ -4228,7 +4129,6 @@ | System.Net.Sockets;SocketAsyncEventArgs;false;set_SendPacketsElements;(System.Net.Sockets.SendPacketsElement[]);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Net.Sockets;SocketAsyncEventArgs;false;set_UserToken;(System.Object);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;SocketException;false;get_Message;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;AcceptAsync;(System.Net.Sockets.Socket,System.Net.Sockets.Socket);;Argument[1];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint);;Argument[1];Argument[0];taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ConnectAsync;(System.Net.Sockets.Socket,System.Net.EndPoint,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated | @@ -4240,8 +4140,6 @@ | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[1];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;ReceiveAsync;(System.Net.Sockets.Socket,System.Memory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;ReceiveFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated | -| System.Net.Sockets;SocketTaskExtensions;false;ReceiveMessageFromAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendAsync;(System.Net.Sockets.Socket,System.ReadOnlyMemory,System.Net.Sockets.SocketFlags,System.Threading.CancellationToken);;Argument[3];ReturnValue;taint;generated | | System.Net.Sockets;SocketTaskExtensions;false;SendToAsync;(System.Net.Sockets.Socket,System.ArraySegment,System.Net.Sockets.SocketFlags,System.Net.EndPoint);;Argument[3];Argument[0];taint;generated | @@ -4252,8 +4150,6 @@ | System.Net.Sockets;TcpClient;false;set_Client;(System.Net.Sockets.Socket);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;AcceptSocket;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net.Sockets;TcpListener;false;AcceptTcpClient;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net.Sockets;TcpListener;false;EndAcceptSocket;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net.Sockets;TcpListener;false;EndAcceptTcpClient;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPAddress,System.Int32);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;TcpListener;(System.Net.IPEndPoint);;Argument[0];Argument[Qualifier];taint;generated | | System.Net.Sockets;TcpListener;false;get_LocalEndpoint;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4325,17 +4221,11 @@ | System.Net;CookieCollection;false;get_SyncRoot;();;Argument[Qualifier];ReturnValue;value;generated | | System.Net;CookieException;false;GetObjectData;(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext);;Argument[Qualifier];Argument[0];taint;generated | | System.Net;CredentialCache;false;GetCredential;(System.Uri,System.String);;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net;Dns;false;EndGetHostAddresses;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndGetHostByName;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndGetHostEntry;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;Dns;false;EndResolve;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;DnsEndPoint;false;DnsEndPoint;(System.String,System.Int32,System.Net.Sockets.AddressFamily);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;DnsEndPoint;false;ToString;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DnsEndPoint;false;get_Host;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DownloadDataCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;DownloadStringCompletedEventArgs;false;get_Result;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Net;FileWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;FileWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;FileWebRequest;false;get_ContentType;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4410,9 +4300,6 @@ | System.Net;HttpListenerTimeoutManager;false;get_IdleConnection;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpListenerTimeoutManager;false;set_DrainEntityBody;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;HttpListenerTimeoutManager;false;set_IdleConnection;(System.TimeSpan);;Argument[0];Argument[Qualifier];taint;generated | -| System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;HttpWebRequest;false;EndGetRequestStream;(System.IAsyncResult,System.Net.TransportContext);;Argument[0];ReturnValue;taint;generated | -| System.Net;HttpWebRequest;false;EndGetResponse;(System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetRequestStream;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetRequestStream;(System.Net.TransportContext);;Argument[Qualifier];ReturnValue;taint;generated | | System.Net;HttpWebRequest;false;GetResponse;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -4498,8 +4385,6 @@ | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest);;Argument[0];ReturnValue;taint;generated | | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[0];ReturnValue;taint;generated | -| System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];Argument[Qualifier];taint;generated | -| System.Net;WebClient;false;GetWebResponse;(System.Net.WebRequest,System.IAsyncResult);;Argument[1];ReturnValue;taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Net;WebClient;false;OpenRead;(System.String);;Argument[Qualifier];ReturnValue;taint;generated | @@ -5368,7 +5253,6 @@ | System.Resources;ResourceReader;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Resources;ResourceReader;false;GetResourceData;(System.String,System.String,System.Byte[]);;Argument[Qualifier];ReturnValue;taint;generated | | System.Resources;ResourceReader;false;ResourceReader;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | -| System.Resources;ResourceSet;false;GetEnumerator;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Resources;ResourceSet;false;ResourceSet;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | | System.Resources;ResourceSet;false;ResourceSet;(System.Resources.IResourceReader);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Resources;ResourceWriter;false;ResourceWriter;(System.IO.Stream);;Argument[0];Argument[Qualifier];taint;generated | @@ -5733,7 +5617,6 @@ | System.Security.Cryptography.X509Certificates;X509Certificate;false;ToString;(System.Boolean);;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Issuer;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509Certificate;false;get_Subject;();;Argument[Qualifier];ReturnValue;taint;generated | -| System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;X509CertificateEnumerator;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier];taint;generated | | System.Security.Cryptography.X509Certificates;X509CertificateCollection+X509CertificateEnumerator;false;get_Current;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;Add;(System.Security.Cryptography.X509Certificates.X509Certificate);;Argument[0];Argument[Qualifier].Element;value;manual | | System.Security.Cryptography.X509Certificates;X509CertificateCollection;false;AddRange;(System.Security.Cryptography.X509Certificates.X509CertificateCollection);;Argument[0].Element;Argument[Qualifier].Element;value;manual | @@ -6224,10 +6107,6 @@ | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;Receive<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan);;Argument[0];ReturnValue;taint;generated | -| System.Threading.Tasks.Dataflow;DataflowBlock;false;ReceiveAsync<>;(System.Threading.Tasks.Dataflow.ISourceBlock,System.TimeSpan,System.Threading.CancellationToken);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;SendAsync<>;(System.Threading.Tasks.Dataflow.ITargetBlock,TInput,System.Threading.CancellationToken);;Argument[1];Argument[0];taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlock;false;TryReceive<>;(System.Threading.Tasks.Dataflow.IReceivableSourceBlock,TOutput);;Argument[0];ReturnValue;taint;generated | | System.Threading.Tasks.Dataflow;DataflowBlockOptions;false;get_CancellationToken;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -7010,12 +6889,9 @@ | System.Xml.Schema;XmlSchemaSet;false;Add;(System.String,System.Xml.XmlReader);;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;Add;(System.Xml.Schema.XmlSchemaSet);;Argument[0];Argument[Qualifier];taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;CopyTo;(System.Xml.Schema.XmlSchema[],System.Int32);;Argument[Qualifier];Argument[0].Element;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Remove;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;Reprocess;(System.Xml.Schema.XmlSchema);;Argument[0];ReturnValue;taint;generated | -| System.Xml.Schema;XmlSchemaSet;false;Schemas;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;XmlSchemaSet;(System.Xml.XmlNameTable);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Schema;XmlSchemaSet;false;get_CompilationSettings;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaSet;false;get_GlobalAttributes;();;Argument[Qualifier];ReturnValue;taint;generated | @@ -7102,10 +6978,8 @@ | System.Xml.Schema;XmlSchemaXPath;false;get_XPath;();;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Schema;XmlSchemaXPath;false;set_XPath;(System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;CodeIdentifiers;false;Add;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[0];ReturnValue;taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;CodeIdentifiers;false;AddUnique;(System.String,System.Object);;Argument[Qualifier];Argument[1];taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;MakeUnique;(System.String);;Argument[0];ReturnValue;taint;generated | | System.Xml.Serialization;CodeIdentifiers;false;ToArray;(System.Type);;Argument[Qualifier];ReturnValue;taint;generated | | System.Xml.Serialization;ImportContext;false;ImportContext;(System.Xml.Serialization.CodeIdentifiers,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | @@ -7411,7 +7285,6 @@ | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteElementStringRaw;(System.String,System.String,System.Xml.XmlQualifiedName);;Argument[1];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteId;(System.Object);;Argument[Qualifier];Argument[0];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncoded;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.Byte[],System.Xml.XmlQualifiedName);;Argument[2].Element;Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringEncodedRaw;(System.String,System.String,System.String,System.Xml.XmlQualifiedName);;Argument[2];Argument[Qualifier];taint;generated | @@ -7419,15 +7292,9 @@ | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.Byte[]);;Argument[2].Element;Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteNullableStringLiteralRaw;(System.String,System.String,System.String);;Argument[2];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[2];Argument[Qualifier];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WritePotentiallyReferencingElement;(System.String,System.String,System.Object,System.Type,System.Boolean,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object);;Argument[Qualifier];Argument[2];taint;generated | -| System.Xml.Serialization;XmlSerializationWriter;false;WriteReferencingElement;(System.String,System.String,System.Object,System.Boolean);;Argument[Qualifier];Argument[2];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[0];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteRpcResult;(System.String,System.String);;Argument[1];Argument[Qualifier];taint;generated | | System.Xml.Serialization;XmlSerializationWriter;false;WriteSerializable;(System.Xml.Serialization.IXmlSerializable,System.String,System.String,System.Boolean);;Argument[0];Argument[Qualifier];taint;generated | @@ -9087,27 +8954,6 @@ | System;TupleExtensions;false;Deconstruct<,>;(System.Tuple,T1,T2);;Argument[0].Property[System.Tuple<,>.Item1];Argument[1];value;manual | | System;TupleExtensions;false;Deconstruct<,>;(System.Tuple,T1,T2);;Argument[0].Property[System.Tuple<,>.Item2];Argument[2];value;manual | | System;TupleExtensions;false;Deconstruct<>;(System.Tuple,T1);;Argument[0].Property[System.Tuple<>.Item1];Argument[1];value;manual | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,,>;(System.ValueTuple>>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,,>;(System.ValueTuple>);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<,>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | -| System;TupleExtensions;false;ToTuple<>;(System.ValueTuple);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | | System;TupleExtensions;false;ToValueTuple<,,,,,,,,,,,,,,,,,,>;(System.Tuple>>);;Argument[0];ReturnValue;taint;generated | diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs index 23980864339..0c9c58d0d23 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.cs @@ -46,6 +46,9 @@ namespace HardcodedSymmetricEncryptionKey // GOOD (this function hashes password) var de = DecryptWithPassword(ct, c, iv); + // BAD: hard-coded password passed to Decrypt + var de1 = Decrypt(ct, c, iv); + // BAD [NOT DETECTED] CreateCryptographicKey(null, byteArrayFromString); @@ -53,6 +56,26 @@ namespace HardcodedSymmetricEncryptionKey CreateCryptographicKey(null, File.ReadAllBytes("secret.key")); } + public static string Decrypt(byte[] cipherText, byte[] password, byte[] IV) + { + byte[] rawPlaintext; + var salt = new byte[] { 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00 }; + + using (Aes aes = new AesManaged()) + { + using (MemoryStream ms = new MemoryStream()) + { + using (CryptoStream cs = new CryptoStream(ms, aes.CreateDecryptor(password, IV), CryptoStreamMode.Write)) + { + cs.Write(cipherText, 0, cipherText.Length); + } + rawPlaintext = ms.ToArray(); + } + + return Encoding.Unicode.GetString(rawPlaintext); + } + } + public static string DecryptWithPassword(byte[] cipherText, byte[] password, byte[] IV) { byte[] rawPlaintext; diff --git a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected index fd9dd378fa5..676148bd7ed 100644 --- a/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected +++ b/csharp/ql/test/query-tests/Security Features/CWE-321/HardcodedSymmetricEncryptionKey/HardcodedSymmetricEncryptionKey.expected @@ -1,6 +1,7 @@ | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:17:21:17:97 | array creation of type Byte[] | key | | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:22:23:22:99 | array creation of type Byte[] | key | | HardcodedSymmetricEncryptionKey.cs:31:21:31:21 | access to local variable d | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | -| HardcodedSymmetricEncryptionKey.cs:85:23:85:25 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | -| HardcodedSymmetricEncryptionKey.cs:98:87:98:89 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | -| HardcodedSymmetricEncryptionKey.cs:98:87:98:89 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" | key | +| HardcodedSymmetricEncryptionKey.cs:68:87:68:94 | access to parameter password | Hard-coded symmetric $@ is used in symmetric algorithm in Decryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:108:23:108:25 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Key property assignment | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:25:21:25:97 | array creation of type Byte[] | key | +| HardcodedSymmetricEncryptionKey.cs:121:87:121:89 | access to parameter key | Hard-coded symmetric $@ is used in symmetric algorithm in Encryptor(rgbKey, IV) | HardcodedSymmetricEncryptionKey.cs:28:62:28:115 | "Hello, world: here is a very bad way to create a key" | key | diff --git a/csharp/tools/tracing-config.lua b/csharp/tools/tracing-config.lua index b4ff0206b02..efa936b41dd 100644 --- a/csharp/tools/tracing-config.lua +++ b/csharp/tools/tracing-config.lua @@ -2,7 +2,54 @@ function RegisterExtractorPack(id) local extractor = GetPlatformToolsDirectory() .. 'Semmle.Extraction.CSharp.Driver' if OperatingSystem == 'windows' then extractor = extractor .. '.exe' end + + function DotnetMatcherBuild(compilerName, compilerPath, compilerArguments, + _languageId) + if compilerName ~= 'dotnet' and compilerName ~= 'dotnet.exe' then + return nil + end + + -- The dotnet CLI has the following usage instructions: + -- dotnet [sdk-options] [command] [command-options] [arguments] + -- we are interested in dotnet build, which has the following usage instructions: + -- dotnet [options] build [...] + -- For now, parse the command line as follows: + -- Everything that starts with `-` (or `/`) will be ignored. + -- The first non-option argument is treated as the command. + -- if that's `build`, we append `/p:UseSharedCompilation=false` to the command line, + -- otherwise we do nothing. + local match = false + local argv = compilerArguments.argv + if OperatingSystem == 'windows' then + -- let's hope that this split matches the escaping rules `dotnet` applies to command line arguments + -- or, at least, that it is close enough + argv = + NativeArgumentsToArgv(compilerArguments.nativeArgumentPointer) + end + for i, arg in ipairs(argv) do + -- dotnet options start with either - or / (both are legal) + local firstCharacter = string.sub(arg, 1, 1) + if not (firstCharacter == '-') and not (firstCharacter == '/') then + Log(1, 'Dotnet subcommand detected: %s', arg) + if arg == 'build' or arg == 'msbuild' then match = true end + break + end + end + if match then + return { + order = ORDER_REPLACE, + invocation = BuildExtractorInvocation(id, compilerPath, + compilerPath, + compilerArguments, nil, { + '/p:UseSharedCompilation=false' + }) + } + end + return nil + end + local windowsMatchers = { + DotnetMatcherBuild, CreatePatternMatcher({'^dotnet%.exe$'}, MatchCompilerName, extractor, { prepend = {'--dotnetexec', '--cil'}, order = ORDER_BEFORE @@ -10,22 +57,21 @@ function RegisterExtractorPack(id) CreatePatternMatcher({'^csc.*%.exe$'}, MatchCompilerName, extractor, { prepend = {'--compiler', '"${compiler}"', '--cil'}, order = ORDER_BEFORE - }), CreatePatternMatcher({'^fakes.*%.exe$', 'moles.*%.exe'}, MatchCompilerName, nil, {trace = false}) } local posixMatchers = { - CreatePatternMatcher({'^mcs%.exe$', '^csc%.exe$'}, MatchCompilerName, - extractor, { - prepend = {'--compiler', '"${compiler}"', '--cil'}, - order = ORDER_BEFORE - - }), + DotnetMatcherBuild, CreatePatternMatcher({'^mono', '^dotnet$'}, MatchCompilerName, extractor, { prepend = {'--dotnetexec', '--cil'}, order = ORDER_BEFORE + }), + CreatePatternMatcher({'^mcs%.exe$', '^csc%.exe$'}, MatchCompilerName, + extractor, { + prepend = {'--compiler', '"${compiler}"', '--cil'}, + order = ORDER_BEFORE }), function(compilerName, compilerPath, compilerArguments, _languageId) if MatchCompilerName('^msbuild$', compilerName, compilerPath, compilerArguments) or @@ -49,7 +95,6 @@ function RegisterExtractorPack(id) else return posixMatchers end - end -- Return a list of minimum supported versions of the configuration file format diff --git a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst index d3b04a32a0b..bb428f2c00d 100644 --- a/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/analyzing-databases-with-the-codeql-cli.rst @@ -135,6 +135,47 @@ pack names and use the ``--download`` flag:: The ``analyze`` command above runs the default suite from ``microsoft/coding-standards v1.0.0`` and the latest version of ``github/security-queries`` on the specified database. For further information about default suites, see ":ref:`Publishing and using CodeQL packs `". +Running a subset of queries in a CodeQL pack +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you are using CodeQL CLI v2.8.1 or later, you can include a path at the end of a pack specification to run a subset of queries inside the pack. This applies to any command that locates or runs queries within a pack. + +The complete way to specify a set of queries is in the form ``scope/name@range:path``, where: + +- ``scope/name`` is the qualified name of a CodeQL pack. +- ``range`` is a `semver range `_. +- ``path`` is a file system path to a single query, a directory containing queries, or a query suite file. + +When you specify a ``scope/name``, the ``range`` and ``path`` are +optional. If you omit a ``range`` then the latest version of the +specified pack is used. If you omit a ``path`` then the default query suite +of the specified pack is used. + +The ``path`` can be one of a ``*.ql`` query file, a directory +containing one or more queries, or a ``.qls`` query suite file. If +you omit a pack name, then you must provide a ``path``, +which will be interpreted relative to the working directory +of the current process. + +If you specify a ``scope/name`` and ``path``, then the ``path`` cannot +be absolute. It is considered relative to the root of the CodeQL +pack. + +To analyze a database using all queries in the `experimental/Security` folder within the `codeql/cpp-queries` CodeQL pack you can use:: + + codeql database analyze --format=sarif-latest --output=results \ + codeql/cpp-queries:experimental/Security + +To run the `RedundantNullCheckParam.ql` query in the `codeql/cpp-queries` CodeQL pack use:: + + codeql database analyze --format=sarif-latest --output=results \ + 'codeql/cpp-queries:experimental/Likely Bugs/RedundantNullCheckParam.ql' + +To analyze your database using the `cpp-security-and-quality.qls` query suite from a version of the `codeql/cpp-queries` CodeQL pack that is >= 0.0.3 and < 0.1.0 (the highest compatible version will be chosen) you can use:: + + codeql database analyze --format=sarif-latest --output=results \ + 'codeql/cpp-queries@~0.0.3:codeql-suites/cpp-security-and-quality.qls' + For more information about CodeQL packs, see :doc:`About CodeQL Packs `. Running query suites @@ -223,7 +264,7 @@ you can include the query help for your custom queries in SARIF files generated After uploading the SARIF file to GitHub, the query help is shown in the code scanning UI for any alerts generated by the custom queries. -From CodeQL CLI 2.7.1 onwards, you can include markdown-rendered query help in SARIF files +From CodeQL CLI v2.7.1 onwards, you can include markdown-rendered query help in SARIF files by providing the ``--sarif-add-query-help`` option when running ``codeql database analyze``. For more information, see `Configuring CodeQL CLI in your CI system `__ diff --git a/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst b/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst index 6373440bcbb..da7a0872803 100644 --- a/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst +++ b/docs/codeql/codeql-cli/creating-and-working-with-codeql-packs.rst @@ -68,3 +68,11 @@ This command downloads all dependencies to the shared cache on the local disk. Note Running the ``codeql pack add`` and ``codeql pack install`` commands will generate or update the ``qlpack.lock.yml`` file. This file should be checked-in to version control. The ``qlpack.lock.yml`` file contains the precise version numbers used by the pack. + +.. pull-quote:: + + Note + + By default ``codeql pack install`` will install dependencies from the Container registry on GitHub.com. + You can install dependencies from a GitHub Enterprise Server Container registry by creating a ``qlconfig.yml`` file. + For more information, see ":doc:`Publishing and using CodeQL packs `." diff --git a/docs/codeql/codeql-cli/exit-codes.rst b/docs/codeql/codeql-cli/exit-codes.rst index 5d9a0d434b6..86a1dfc12dd 100644 --- a/docs/codeql/codeql-cli/exit-codes.rst +++ b/docs/codeql/codeql-cli/exit-codes.rst @@ -71,4 +71,4 @@ Other ----- In the case of really severe problems within the JVM that runs ``codeql``, it might return a nonzero exit code of its own choosing. -This should only happen if something is severely wrong with the CodeQL installation. \ No newline at end of file +This should only happen if something is severely wrong with the CodeQL installation, or if there is a memory issue with the host system running the CodeQL process. For example, Unix systems may return `Exit Code 137` to indicate that the kernel has killed a process that CodeQL has started. One way to troubleshoot this is to modify your `--ram=` flag for the `codeql database analyze` step and re-run your workflow. diff --git a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst index d28e27e10d7..985ca2659f3 100644 --- a/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst +++ b/docs/codeql/codeql-cli/publishing-and-using-codeql-packs.rst @@ -72,3 +72,53 @@ The ``analyze`` command will run the default suite of any specified CodeQL packs :: codeql analyze / / + +Working with CodeQL packs on GitHub Enterprise Server +----------------------------------------------------- + +.. pull-quote:: + + Note + + The Container registry for GitHub Enterprise Server supports CodeQL query packs from GitHub Enterprise Server 3.6 onward. + +By default, the CodeQL CLI expects to download CodeQL packs from and publish packs to the Container registry on GitHub.com. However, you can also work with CodeQL packs in a Container registry on GitHub Enterprise Server 3.6, and later, by creating a ``qlconfig.yml`` file to tell the CLI which Container registry to use for each pack. + +Create a ``~/.codeql/qlconfig.yml`` file using your preferred text editor, and add entries to specify which registry to use for one or more package name patterns. +For example, the following ``qlconfig.yml`` file associates all packs with the Container registry for the GitHub Enterprise Server at ``GHE_HOSTNAME``, except packs matching ``codeql/*``, which are associated with the Container registry on GitHub.com: + +.. code-block:: yaml + + registries: + - packages: 'codeql/*' + url: https://ghcr.io/v2/ + - packages: '*' + url: https://containers.GHE_HOSTNAME/v2/ + +The CodeQL CLI will determine which registry to use for a given package name by finding the first item in the ``registries`` list with a ``packages`` property that matches that package name. +This means that you'll generally want to define the most specific package name patterns first. + +You can now use ``codeql pack publish``, ``codeql pack download``, and ``codeql database analyze`` to manage packs on GitHub Enterprise Server. + +Authenticating to GitHub Container registries +--------------------------------------------- + +You can publish packs and download private packs by authenticating to the appropriate GitHub Container registry. + +You can authenticate to the Container registry on GitHub.com in two ways: + +1. Pass the ``--github-auth-stdin`` option to the CodeQL CLI, then supply a GitHub Apps token or personal access token via standard input. +2. Set the ``GITHUB_TOKEN`` environment variable to a GitHub Apps token or personal access token. + +Similarly, you can authenticate to a GHES Container registry, or authenticate to multiple registries simultaneously (for example, to download or run private packs from multiple registries) in two ways: + +1. Pass the ``--registries-auth-stdin`` option to the CodeQL CLI, then supply a registry authentication string via standard input. +2. Set the ``CODEQL_REGISTRIES_AUTH`` environment variable to a registry authentication string. + +A registry authentication string is a comma-separated list of ``=`` pairs, where ``registry-url`` is a GitHub Container registry URL, such as ``https://containers.GHE_HOSTNAME/v2/``, and ``token`` is a GitHub Apps token or personal access token for that GitHub Container registry. +This ensures that each token is only passed to the Container registry you specify. +For instance, the following registry authentication string specifies that the CodeQL CLI should authenticate to the Container registry on GitHub.com using the token ```` and to the Container registry for the GHES instance at ``GHE_HOSTNAME`` using the token ````: + +.. code-block:: none + + https://ghcr.io/v2/=,https://containers.GHE_HOSTNAME/v2/= diff --git a/docs/codeql/codeql-overview/supported-languages-and-frameworks.rst b/docs/codeql/codeql-overview/supported-languages-and-frameworks.rst index 4353f9402b7..be66125846a 100644 --- a/docs/codeql/codeql-overview/supported-languages-and-frameworks.rst +++ b/docs/codeql/codeql-overview/supported-languages-and-frameworks.rst @@ -11,14 +11,17 @@ CodeQL. Languages and compilers ####################### -CodeQL supports the following languages and compilers. +The current versions of the CodeQL CLI (`changelog `__, `releases `__), +CodeQL library packs (`source `__), +and CodeQL bundle (`releases `__) +support the following languages and compilers. .. include:: ../support/reusables/versions-compilers.rst Frameworks and libraries ######################## -The libraries and queries in the current version of CodeQL have been explicitly checked against the libraries and frameworks listed below. +The current versions of the CodeQL library and query packs (`source `__) have been explicitly checked against the libraries and frameworks listed below. .. pull-quote:: diff --git a/docs/codeql/query-help/cpp.rst b/docs/codeql/query-help/cpp.rst index 7c3cbe304d7..12d2dfbf53e 100644 --- a/docs/codeql/query-help/cpp.rst +++ b/docs/codeql/query-help/cpp.rst @@ -3,7 +3,9 @@ CodeQL query help for C and C++ .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/cpp-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-cpp.rst \ No newline at end of file diff --git a/docs/codeql/query-help/csharp.rst b/docs/codeql/query-help/csharp.rst index 9c5c6351ce3..5d7f732a588 100644 --- a/docs/codeql/query-help/csharp.rst +++ b/docs/codeql/query-help/csharp.rst @@ -3,6 +3,8 @@ CodeQL query help for C# .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/csharp-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-csharp.rst \ No newline at end of file diff --git a/docs/codeql/query-help/go.rst b/docs/codeql/query-help/go.rst index 9e3050f74d0..bcd4aba9b51 100644 --- a/docs/codeql/query-help/go.rst +++ b/docs/codeql/query-help/go.rst @@ -3,6 +3,8 @@ CodeQL query help for Go .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/go-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-go.rst diff --git a/docs/codeql/query-help/java.rst b/docs/codeql/query-help/java.rst index 8af2ee52890..4876546d2dc 100644 --- a/docs/codeql/query-help/java.rst +++ b/docs/codeql/query-help/java.rst @@ -3,6 +3,8 @@ CodeQL query help for Java .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/java-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-java.rst diff --git a/docs/codeql/query-help/javascript.rst b/docs/codeql/query-help/javascript.rst index d7cf6797852..58fe97eb3b0 100644 --- a/docs/codeql/query-help/javascript.rst +++ b/docs/codeql/query-help/javascript.rst @@ -3,6 +3,8 @@ CodeQL query help for JavaScript .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/javascript-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-javascript.rst \ No newline at end of file diff --git a/docs/codeql/query-help/python.rst b/docs/codeql/query-help/python.rst index da68c1caa9b..9d915d443f3 100644 --- a/docs/codeql/query-help/python.rst +++ b/docs/codeql/query-help/python.rst @@ -3,6 +3,8 @@ CodeQL query help for Python .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/python-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-python.rst \ No newline at end of file diff --git a/docs/codeql/query-help/ruby.rst b/docs/codeql/query-help/ruby.rst index 3ce1304ba76..e733aecaf79 100644 --- a/docs/codeql/query-help/ruby.rst +++ b/docs/codeql/query-help/ruby.rst @@ -3,6 +3,8 @@ CodeQL query help for Ruby .. include:: ../reusables/query-help-overview.rst -For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. +These queries are published in the CodeQL query pack ``codeql/ruby-queries`` (`changelog `__, `source `__). + +For shorter queries that you can use as building blocks when writing your own queries, see the `example queries in the CodeQL repository `__. .. include:: toc-ruby.rst diff --git a/docs/codeql/support/reusables/frameworks.rst b/docs/codeql/support/reusables/frameworks.rst index 12bcd5af8e6..fc5410648cf 100644 --- a/docs/codeql/support/reusables/frameworks.rst +++ b/docs/codeql/support/reusables/frameworks.rst @@ -1,6 +1,10 @@ C and C++ built-in support ================================ +Provided by the current versions of the +CodeQL query pack ``codeql/cpp-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/cpp-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable @@ -14,6 +18,10 @@ C and C++ built-in support C# built-in support ================================ +Provided by the current versions of the +CodeQL query pack ``codeql/csharp-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/csharp-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable @@ -33,6 +41,10 @@ C# built-in support Go built-in support ================================ +Provided by the current versions of the +CodeQL query pack ``codeql/go-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/go-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable @@ -84,6 +96,10 @@ Go built-in support Java built-in support ================================== +Provided by the current versions of the +CodeQL query pack ``codeql/java-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/java-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable @@ -113,6 +129,10 @@ Java built-in support JavaScript and TypeScript built-in support ======================================================= +Provided by the current versions of the +CodeQL query pack ``codeql/javascript-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/javascript-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable @@ -156,6 +176,10 @@ JavaScript and TypeScript built-in support Python built-in support ==================================== +Provided by the current versions of the +CodeQL query pack ``codeql/python-queries`` (`changelog `__, `source `__) +and the CodeQL library pack ``codeql/python-all`` (`changelog `__, `source `__). + .. csv-table:: :header-rows: 1 :class: fullWidthTable diff --git a/go/ql/lib/CHANGELOG.md b/go/ql/lib/CHANGELOG.md index 112f4fab585..a4ead0ef794 100644 --- a/go/ql/lib/CHANGELOG.md +++ b/go/ql/lib/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2 + +## 0.2.1 + ## 0.2.0 ### Deprecated APIs diff --git a/go/ql/lib/change-notes/released/0.2.1.md b/go/ql/lib/change-notes/released/0.2.1.md new file mode 100644 index 00000000000..c260de2a9ee --- /dev/null +++ b/go/ql/lib/change-notes/released/0.2.1.md @@ -0,0 +1 @@ +## 0.2.1 diff --git a/go/ql/lib/change-notes/released/0.2.2.md b/go/ql/lib/change-notes/released/0.2.2.md new file mode 100644 index 00000000000..fc31cbd3d6f --- /dev/null +++ b/go/ql/lib/change-notes/released/0.2.2.md @@ -0,0 +1 @@ +## 0.2.2 diff --git a/go/ql/lib/codeql-pack.release.yml b/go/ql/lib/codeql-pack.release.yml index 5274e27ed52..16a06790aa8 100644 --- a/go/ql/lib/codeql-pack.release.yml +++ b/go/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.2.2 diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index 964f5d4dd13..789f504c667 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-all -version: 0.2.1-dev +version: 0.2.3-dev groups: go dbscheme: go.dbscheme extractor: go diff --git a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll index 2fb37ecb3fa..61499340de3 100644 --- a/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll +++ b/go/ql/lib/semmle/go/security/TaintedPathCustomizations.qll @@ -70,6 +70,15 @@ module TaintedPath { PathAsSink() { this = any(FileSystemAccess fsa).getAPathArgument() } } + /** + * A numeric- or boolean-typed node, considered a sanitizer for path traversal. + */ + class NumericOrBooleanSanitizer extends Sanitizer { + NumericOrBooleanSanitizer() { + this.getType() instanceof NumericType or this.getType() instanceof BoolType + } + } + /** * A call to `filepath.Rel`, considered as a sanitizer for path traversal. */ diff --git a/go/ql/src/CHANGELOG.md b/go/ql/src/CHANGELOG.md index bed2509f5d3..c981e074fad 100644 --- a/go/ql/src/CHANGELOG.md +++ b/go/ql/src/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.2.2 + +## 0.2.1 + ## 0.2.0 ## 0.1.4 diff --git a/go/ql/src/RedundantCode/DuplicateSwitchCase.ql b/go/ql/src/RedundantCode/DuplicateSwitchCase.ql index 46956347785..7af97b76875 100644 --- a/go/ql/src/RedundantCode/DuplicateSwitchCase.ql +++ b/go/ql/src/RedundantCode/DuplicateSwitchCase.ql @@ -13,7 +13,7 @@ import go -/** Gets the global value number of of `e`, which is the `i`th case label of `switch`. */ +/** Gets the global value number of `e`, which is the `i`th case label of `switch`. */ GVN switchCaseGVN(SwitchStmt switch, int i, Expr e) { e = switch.getCase(i).getExpr(0) and result = e.getGlobalValueNumber() } diff --git a/go/ql/src/change-notes/2022-08-02-path-injection-sanitizer.md b/go/ql/src/change-notes/2022-08-02-path-injection-sanitizer.md new file mode 100644 index 00000000000..1c45e8d14e5 --- /dev/null +++ b/go/ql/src/change-notes/2022-08-02-path-injection-sanitizer.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The query `go/path-injection` no longer considers user-controlled numeric or boolean-typed data as potentially dangerous. diff --git a/go/ql/src/change-notes/released/0.2.1.md b/go/ql/src/change-notes/released/0.2.1.md new file mode 100644 index 00000000000..c260de2a9ee --- /dev/null +++ b/go/ql/src/change-notes/released/0.2.1.md @@ -0,0 +1 @@ +## 0.2.1 diff --git a/go/ql/src/change-notes/released/0.2.2.md b/go/ql/src/change-notes/released/0.2.2.md new file mode 100644 index 00000000000..fc31cbd3d6f --- /dev/null +++ b/go/ql/src/change-notes/released/0.2.2.md @@ -0,0 +1 @@ +## 0.2.2 diff --git a/go/ql/src/codeql-pack.release.yml b/go/ql/src/codeql-pack.release.yml index 5274e27ed52..16a06790aa8 100644 --- a/go/ql/src/codeql-pack.release.yml +++ b/go/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.2.2 diff --git a/go/ql/lib/definitions.ql b/go/ql/src/definitions.ql similarity index 100% rename from go/ql/lib/definitions.ql rename to go/ql/src/definitions.ql diff --git a/go/ql/src/qlpack.yml b/go/ql/src/qlpack.yml index 80a4430b8da..b19c723b9c7 100644 --- a/go/ql/src/qlpack.yml +++ b/go/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/go-queries -version: 0.2.1-dev +version: 0.2.3-dev groups: - go - queries diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index 06ec8d978d4..598035e03c3 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -36,7 +36,7 @@ java.lang,13,,58,,,,,,,,,,,8,,,,,4,,,1,,,,,,,,,,,,,,,46,12 java.net,10,3,7,,,,,,,,,,,,,,10,,,,,,,,,,,,,,,,,,,3,7, java.nio,15,,6,,13,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,,,6, java.sql,11,,,,,,,,,4,,,,,,,,,,,,,,,,7,,,,,,,,,,,, -java.util,44,,438,,,,,,,,,,,34,,,,,,5,2,,1,2,,,,,,,,,,,,,24,414 +java.util,44,,458,,,,,,,,,,,34,,,,,,5,2,,1,2,,,,,,,,,,,,,36,422 javax.faces.context,2,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,7,, javax.jms,,9,57,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,9,57, javax.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23 diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index 7b45a3115b1..06101b6633d 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -15,9 +15,9 @@ Java framework & library support `Apache HttpComponents `_,"``org.apache.hc.core5.*``, ``org.apache.http``",5,136,28,,,3,,,,25 `Google Guava `_,``com.google.common.*``,,728,39,,6,,,,, `JSON-java `_,``org.json``,,236,,,,,,,, - Java Standard Library,``java.*``,3,549,130,28,,,7,,,10 + Java Standard Library,``java.*``,3,569,130,28,,,7,,,10 Java extensions,"``javax.*``, ``jakarta.*``",63,609,32,,,4,,1,1,2 `Spring `_,``org.springframework.*``,29,476,101,,,,19,14,,29 Others,"``androidx.slice``, ``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``kotlin.jvm.internal``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.logging.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jboss.logging``, ``org.jdbi.v3.core``, ``org.jooq``, ``org.mvel2``, ``org.scijava.log``, ``org.slf4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",65,395,932,,,,14,18,,3 - Totals,,217,6410,1474,117,6,10,107,33,1,84 + Totals,,217,6430,1474,117,6,10,107,33,1,84 diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 21b35fbcdd1..4100594ad35 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -134,7 +134,7 @@ open class KotlinFileExtractor( is IrProperty -> { val parentId = useDeclarationParent(declaration.parent, false)?.cast() if (parentId != null) { - extractProperty(declaration, parentId, extractBackingField = true, extractFunctionBodies = extractFunctionBodies, null, listOf()) + extractProperty(declaration, parentId, extractBackingField = true, extractFunctionBodies = extractFunctionBodies, extractPrivateMembers = extractPrivateMembers, null, listOf()) } Unit } @@ -364,7 +364,7 @@ open class KotlinFileExtractor( if (shouldExtractDecl(it, false)) { when(it) { is IrFunction -> extractFunction(it, id, extractBody = false, extractMethodAndParameterTypeAccesses = false, typeParamSubstitution, argsIncludingOuterClasses) - is IrProperty -> extractProperty(it, id, extractBackingField = false, extractFunctionBodies = false, typeParamSubstitution, argsIncludingOuterClasses) + is IrProperty -> extractProperty(it, id, extractBackingField = false, extractFunctionBodies = false, extractPrivateMembers = false, typeParamSubstitution, argsIncludingOuterClasses) else -> {} } } @@ -888,6 +888,10 @@ open class KotlinFileExtractor( if (shortName.nameInDB != shortName.kotlinName) { tw.writeKtFunctionOriginalNames(methodId, shortName.kotlinName) } + + if (f.hasInterfaceParent() && f.body != null) { + addModifiers(id, "default") // The actual output class file may or may not have this modifier, depending on the -Xjvm-default setting. + } } tw.writeHasLocation(id, locId) @@ -955,7 +959,7 @@ open class KotlinFileExtractor( return id } - private fun extractProperty(p: IrProperty, parentId: Label, extractBackingField: Boolean, extractFunctionBodies: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?) { + private fun extractProperty(p: IrProperty, parentId: Label, extractBackingField: Boolean, extractFunctionBodies: Boolean, extractPrivateMembers: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List?) { with("property", p) { if (isFake(p)) return @@ -970,7 +974,11 @@ open class KotlinFileExtractor( val getter = p.getter val setter = p.setter - if (getter != null) { + if (getter == null) { + if (p.modality != Modality.FINAL || !isExternalDeclaration(p)) { + logger.warnElement("IrProperty without a getter", p) + } + } else if (shouldExtractDecl(getter, extractPrivateMembers)) { val getterId = extractFunction(getter, parentId, extractBody = extractFunctionBodies, extractMethodAndParameterTypeAccesses = extractFunctionBodies, typeSubstitution, classTypeArgsIncludingOuterClasses)?.cast() if (getterId != null) { tw.writeKtPropertyGetters(id, getterId) @@ -978,13 +986,13 @@ open class KotlinFileExtractor( tw.writeCompiler_generated(getterId, CompilerGeneratedKinds.DELEGATED_PROPERTY_GETTER.kind) } } - } else { - if (p.modality != Modality.FINAL || !isExternalDeclaration(p)) { - logger.warnElement("IrProperty without a getter", p) - } } - if (setter != null) { + if (setter == null) { + if (p.isVar && !isExternalDeclaration(p)) { + logger.warnElement("isVar property without a setter", p) + } + } else if (shouldExtractDecl(setter, extractPrivateMembers)) { if (!p.isVar) { logger.warnElement("!isVar property with a setter", p) } @@ -995,10 +1003,6 @@ open class KotlinFileExtractor( tw.writeCompiler_generated(setterId, CompilerGeneratedKinds.DELEGATED_PROPERTY_SETTER.kind) } } - } else { - if (p.isVar && !isExternalDeclaration(p)) { - logger.warnElement("isVar property without a setter", p) - } } if (bf != null && extractBackingField) { @@ -1386,7 +1390,8 @@ open class KotlinFileExtractor( dispatchReceiver: IrExpression?, extensionReceiver: IrExpression?, typeArguments: List = listOf(), - extractClassTypeArguments: Boolean = false) { + extractClassTypeArguments: Boolean = false, + superQualifierSymbol: IrClassSymbol? = null) { val locId = tw.getLocation(callsite) @@ -1404,7 +1409,8 @@ open class KotlinFileExtractor( dispatchReceiver?.let { { callId -> extractExpressionExpr(dispatchReceiver, enclosingCallable, callId, -1, enclosingStmt) } }, extensionReceiver?.let { { argParent -> extractExpressionExpr(extensionReceiver, enclosingCallable, argParent, 0, enclosingStmt) } }, typeArguments, - extractClassTypeArguments + extractClassTypeArguments, + superQualifierSymbol ) } @@ -1424,7 +1430,8 @@ open class KotlinFileExtractor( extractDispatchReceiver: ((Label) -> Unit)?, extractExtensionReceiver: ((Label) -> Unit)?, typeArguments: List = listOf(), - extractClassTypeArguments: Boolean = false) { + extractClassTypeArguments: Boolean = false, + superQualifierSymbol: IrClassSymbol? = null) { val callTarget = syntacticCallTarget.target.realOverrideTarget val id = tw.getFreshIdLabel() @@ -1483,6 +1490,8 @@ open class KotlinFileExtractor( if (callTarget.shouldExtractAsStatic) { extractStaticTypeAccessQualifier(callTarget, id, locId, enclosingCallable, enclosingStmt) + } else if (superQualifierSymbol != null) { + extractSuperAccess(superQualifierSymbol.typeWith(), enclosingCallable, id, -1, enclosingStmt, locId) } else if (extractDispatchReceiver != null) { extractDispatchReceiver(id) } @@ -1744,7 +1753,7 @@ open class KotlinFileExtractor( else listOf() - extractRawMethodAccess(syntacticCallTarget, c, callable, parent, idx, enclosingStmt, (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, c.dispatchReceiver, c.extensionReceiver, typeArgs, extractClassTypeArguments) + extractRawMethodAccess(syntacticCallTarget, c, callable, parent, idx, enclosingStmt, (0 until c.valueArgumentsCount).map { c.getValueArgument(it) }, c.dispatchReceiver, c.extensionReceiver, typeArgs, extractClassTypeArguments, c.superQualifierSymbol) } fun extractSpecialEnumFunction(fnName: String){ @@ -2124,7 +2133,13 @@ open class KotlinFileExtractor( } isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "iterator") && c.origin == IrStatementOrigin.FOR_LOOP_ITERATOR -> { findTopLevelFunctionOrWarn("kotlin.jvm.internal.iterator", "kotlin.jvm.internal.ArrayIteratorKt", c)?.let { iteratorFn -> - extractRawMethodAccess(iteratorFn, c, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, listOf((c.dispatchReceiver!!.type as IrSimpleType).arguments.first().typeOrNull!!)) + val typeArgs = (c.dispatchReceiver!!.type as IrSimpleType).arguments.map { + when(it) { + is IrTypeProjection -> it.type + else -> pluginContext.irBuiltIns.anyNType + } + } + extractRawMethodAccess(iteratorFn, c, callable, parent, idx, enclosingStmt, listOf(c.dispatchReceiver), null, null, typeArgs) } } isFunction(target, "kotlin", "(some array type)", { isArrayType(it) }, "get") && c.origin == IrStatementOrigin.GET_ARRAY_ELEMENT -> { @@ -3060,6 +3075,17 @@ open class KotlinFileExtractor( } } + private fun extractSuperAccess(irType: IrType, callable: Label, parent: Label, idx: Int, enclosingStmt: Label, locId: Label) = + tw.getFreshIdLabel().also { + val type = useType(irType) + tw.writeExprs_superaccess(it, type.javaResult.id, parent, idx) + tw.writeExprsKotlinType(it, type.kotlinResult.id) + tw.writeHasLocation(it, locId) + tw.writeCallableEnclosingExpr(it, callable) + tw.writeStatementEnclosingExpr(it, enclosingStmt) + extractTypeAccessRecursive(irType, locId, it, 0) + } + private fun extractThisAccess(e: IrGetValue, exprParent: ExprParent, callable: Label) { val containingDeclaration = declarationStack.peek() val locId = tw.getLocation(e) @@ -4014,7 +4040,7 @@ open class KotlinFileExtractor( /** * Extracts a single wildcard type access expression with no enclosing callable and statement. */ - private fun extractWildcardTypeAccess(type: TypeResults, location: Label, parent: Label, idx: Int): Label { + private fun extractWildcardTypeAccess(type: TypeResults, location: Label, parent: Label, idx: Int): Label { val id = tw.getFreshIdLabel() tw.writeExprs_wildcardtypeaccess(id, type.javaResult.id, parent, idx) tw.writeExprsKotlinType(id, type.kotlinResult.id) @@ -4025,7 +4051,7 @@ open class KotlinFileExtractor( /** * Extracts a single type access expression with no enclosing callable and statement. */ - private fun extractTypeAccess(type: TypeResults, location: Label, parent: Label, idx: Int): Label { + private fun extractTypeAccess(type: TypeResults, location: Label, parent: Label, idx: Int): Label { // TODO: elementForLocation allows us to give some sort of // location, but a proper location for the type access will // require upstream changes @@ -4051,7 +4077,7 @@ open class KotlinFileExtractor( * `extractTypeAccessRecursive` if the argument is invariant. * No enclosing callable and statement is extracted, this is useful for type access extraction in field declarations. */ - private fun extractWildcardTypeAccessRecursive(t: IrTypeArgument, location: Label, parent: Label, idx: Int) { + private fun extractWildcardTypeAccessRecursive(t: IrTypeArgument, location: Label, parent: Label, idx: Int) { val typeLabels by lazy { TypeResults(getTypeArgumentLabel(t), TypeResult(fakeKotlinType(), "TODO", "TODO")) } when (t) { is IrStarProjection -> extractWildcardTypeAccess(typeLabels, location, parent, idx) @@ -4071,7 +4097,7 @@ open class KotlinFileExtractor( * Extracts a type access expression and its child type access expressions in case of a generic type. Nested generics are also handled. * No enclosing callable and statement is extracted, this is useful for type access extraction in field declarations. */ - private fun extractTypeAccessRecursive(t: IrType, location: Label, parent: Label, idx: Int, typeContext: TypeContext = TypeContext.OTHER): Label { + private fun extractTypeAccessRecursive(t: IrType, location: Label, parent: Label, idx: Int, typeContext: TypeContext = TypeContext.OTHER): Label { val typeAccessId = extractTypeAccess(useType(t, typeContext), location, parent, idx) if (t is IrSimpleType) { t.arguments.forEachIndexed { argIdx, arg -> diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 982a11c5f68..22bae8b55be 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.backend.common.lower.parents import org.jetbrains.kotlin.backend.common.lower.parentsWithSelf import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.codegen.JvmCodegenUtil import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.declarations.* @@ -23,8 +24,10 @@ import org.jetbrains.kotlin.load.java.BuiltinMethodsWithSpecialGenericSignature import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.java.sources.JavaSourceElement import org.jetbrains.kotlin.load.java.structure.* +import org.jetbrains.kotlin.load.kotlin.getJvmModuleNameForDeserializedDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.NameUtils import org.jetbrains.kotlin.name.SpecialNames import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.util.OperatorNameConventions @@ -108,14 +111,31 @@ open class KotlinUsesExtractor( } data class TypeResults(val javaResult: TypeResult, val kotlinResult: TypeResult) - fun useType(t: IrType, context: TypeContext = TypeContext.OTHER) = + fun useType(t: IrType, context: TypeContext = TypeContext.OTHER): TypeResults { when(t) { - is IrSimpleType -> useSimpleType(t, context) + is IrSimpleType -> return useSimpleType(t, context) else -> { logger.error("Unrecognised IrType: " + t.javaClass) - TypeResults(TypeResult(fakeLabel(), "unknown", "unknown"), TypeResult(fakeLabel(), "unknown", "unknown")) + return extractErrorType() } } + } + + private fun extractJavaErrorType(): TypeResult { + val typeId = tw.getLabelFor("@\"errorType\"") { + tw.writeError_type(it) + } + return TypeResult(typeId, null, "") + } + + private fun extractErrorType(): TypeResults { + val javaResult = extractJavaErrorType() + val kotlinTypeId = tw.getLabelFor("@\"errorKotlinType\"") { + tw.writeKt_nullable_types(it, javaResult.id) + } + return TypeResults(javaResult, + TypeResult(kotlinTypeId, null, "")) + } fun getJavaEquivalentClass(c: IrClass) = getJavaEquivalentClassId(c)?.let { pluginContext.referenceClass(it.asSingleFqName()) }?.owner @@ -704,7 +724,7 @@ open class KotlinUsesExtractor( } else -> { logger.error("Unrecognised IrSimpleType: " + s.javaClass + ": " + s.render()) - return TypeResults(TypeResult(fakeLabel(), "unknown", "unknown"), TypeResult(fakeLabel(), "unknown", "unknown")) + return extractErrorType() } } } @@ -754,11 +774,25 @@ open class KotlinUsesExtractor( data class FunctionNames(val nameInDB: String, val kotlinName: String) + @OptIn(ObsoleteDescriptorBasedAPI::class) + private fun getJvmModuleName(f: IrFunction) = + NameUtils.sanitizeAsJavaIdentifier( + getJvmModuleNameForDeserializedDescriptor(f.descriptor) ?: JvmCodegenUtil.getModuleName(pluginContext.moduleDescriptor) + ) + fun getFunctionShortName(f: IrFunction) : FunctionNames { if (f.origin == IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA || f.isAnonymousFunction) return FunctionNames( OperatorNameConventions.INVOKE.asString(), OperatorNameConventions.INVOKE.asString()) + + fun getSuffixIfInternal() = + if (f.visibility == DescriptorVisibilities.INTERNAL) { + "\$" + getJvmModuleName(f) + } else { + "" + } + (f as? IrSimpleFunction)?.correspondingPropertySymbol?.let { val propName = it.owner.name.asString() val getter = it.owner.getter @@ -774,35 +808,26 @@ open class KotlinUsesExtractor( } } - when (f) { - getter -> { - val defaultFunctionName = JvmAbi.getterName(propName) - val defaultDbName = if (getter.visibility == DescriptorVisibilities.PRIVATE && getter.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { - // In JVM these functions don't exist, instead the backing field is accessed directly - defaultFunctionName + "\$private" - } else { - defaultFunctionName - } - return FunctionNames(getJvmName(getter) ?: defaultDbName, defaultFunctionName) - } - setter -> { - val defaultFunctionName = JvmAbi.setterName(propName) - val defaultDbName = if (setter.visibility == DescriptorVisibilities.PRIVATE && setter.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { - // In JVM these functions don't exist, instead the backing field is accessed directly - defaultFunctionName + "\$private" - } else { - defaultFunctionName - } - return FunctionNames(getJvmName(setter) ?: defaultDbName, defaultFunctionName) - } + val maybeFunctionName = when (f) { + getter -> JvmAbi.getterName(propName) + setter -> JvmAbi.setterName(propName) else -> { logger.error( "Function has a corresponding property, but is neither the getter nor the setter" ) + null } } + maybeFunctionName?.let { defaultFunctionName -> + val suffix = if (f.visibility == DescriptorVisibilities.PRIVATE && f.origin == IrDeclarationOrigin.DEFAULT_PROPERTY_ACCESSOR) { + "\$private" + } else { + getSuffixIfInternal() + } + return FunctionNames(getJvmName(f) ?: "$defaultFunctionName$suffix", defaultFunctionName) + } } - return FunctionNames(getJvmName(f) ?: f.name.asString(), f.name.asString()) + return FunctionNames(getJvmName(f) ?: "${f.name.asString()}${getSuffixIfInternal()}", f.name.asString()) } // This excludes class type parameters that show up in (at least) constructors' typeParameters list. @@ -1256,7 +1281,7 @@ open class KotlinUsesExtractor( } else -> { logger.error("Unexpected type argument.") - return TypeResult(fakeLabel(), "unknown", "unknown") + return extractJavaErrorType() } } } diff --git a/java/kotlin-extractor/src/main/kotlin/Label.kt b/java/kotlin-extractor/src/main/kotlin/Label.kt index 24e9a8f691c..10459319a76 100644 --- a/java/kotlin-extractor/src/main/kotlin/Label.kt +++ b/java/kotlin-extractor/src/main/kotlin/Label.kt @@ -29,15 +29,3 @@ class IntLabel(val i: Int): Label { class StringLabel(val name: String): Label { override fun toString(): String = "#$name" } - -// TODO: Remove this and all of its uses -fun fakeLabel(): Label { - if (false) { - println("Fake label") - } else { - val sw = StringWriter() - Exception().printStackTrace(PrintWriter(sw)) - println("Fake label from:\n$sw") - } - return IntLabel(0) -} diff --git a/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt b/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt index 93d3c0534de..03f032f8810 100644 --- a/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/comments/CommentExtractor.kt @@ -16,7 +16,8 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private val file: IrFile, private val fileLabel: Label) { private val tw = fileExtractor.tw private val logger = fileExtractor.logger - private val ktFile = Psi2Ir().getKtFile(file) + private val psi2Ir = Psi2Ir(logger) + private val ktFile = psi2Ir.getKtFile(file) fun extract() { if (ktFile == null) { @@ -85,7 +86,7 @@ class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private v val ownerPsi = getKDocOwner(comment) ?: return val owners = mutableListOf() - file.accept(IrVisitorLookup(ownerPsi, file), owners) + file.accept(IrVisitorLookup(psi2Ir, ownerPsi, file), owners) for (ownerIr in owners) { val ownerLabel = diff --git a/java/kotlin-extractor/src/main/kotlin/utils/IrVisitorLookup.kt b/java/kotlin-extractor/src/main/kotlin/utils/IrVisitorLookup.kt index 0020d00fac4..a25c1a4534f 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/IrVisitorLookup.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/IrVisitorLookup.kt @@ -8,7 +8,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.util.isFakeOverride import org.jetbrains.kotlin.ir.visitors.IrElementVisitor -class IrVisitorLookup(private val psi: PsiElement, private val file: IrFile) : +class IrVisitorLookup(private val psi2Ir: Psi2Ir, private val psi: PsiElement, private val file: IrFile) : IrElementVisitor> { private val location = psi.getLocation() @@ -27,7 +27,7 @@ class IrVisitorLookup(private val psi: PsiElement, private val file: IrFile) : } if (location.contains(elementLocation)) { - val psiElement = Psi2Ir().findPsiElement(element, file) + val psiElement = psi2Ir.findPsiElement(element, file) if (psiElement == psi) { // There can be multiple IrElements that match the same PSI element. data.add(element) @@ -35,4 +35,4 @@ class IrVisitorLookup(private val psi: PsiElement, private val file: IrFile) : } element.acceptChildren(this, data) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt b/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt index 57a3e92e6b2..76d155f8b22 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/JvmNames.kt @@ -51,6 +51,8 @@ private val specialFunctions = mapOf( makeDescription(FqName("java.lang.Number"), "toFloat") to "floatValue", makeDescription(StandardNames.FqNames.number.toSafe(), "toDouble") to "doubleValue", makeDescription(FqName("java.lang.Number"), "toDouble") to "doubleValue", + makeDescription(StandardNames.FqNames.string.toSafe(), "get") to "charAt", + makeDescription(FqName("java.lang.String"), "get") to "charAt", ) private val specialFunctionShortNames = specialFunctions.keys.map { it.functionName }.toSet() diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt index 16ee288d05a..fce55c87cd6 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_4_32/Psi2Ir.kt @@ -1,21 +1,19 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi2ir.PsiSourceManager - -class Psi2Ir : Psi2IrFacade { - companion object { - val psiManager = PsiSourceManager() - } +class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { - return psiManager.getKtFile(irFile) + logger.warn("Comment extraction is not supported for Kotlin < 1.5.20") + return null } override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { - return psiManager.findPsiElement(irElement, irFile) + logger.error("Attempted comment extraction for Kotlin < 1.5.20") + return null } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt index 16ee288d05a..fce55c87cd6 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_0/Psi2Ir.kt @@ -1,21 +1,19 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi2ir.PsiSourceManager - -class Psi2Ir : Psi2IrFacade { - companion object { - val psiManager = PsiSourceManager() - } +class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { - return psiManager.getKtFile(irFile) + logger.warn("Comment extraction is not supported for Kotlin < 1.5.20") + return null } override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { - return psiManager.findPsiElement(irElement, irFile) + logger.error("Attempted comment extraction for Kotlin < 1.5.20") + return null } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt index 16ee288d05a..fce55c87cd6 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_10/Psi2Ir.kt @@ -1,21 +1,19 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi2ir.PsiSourceManager - -class Psi2Ir : Psi2IrFacade { - companion object { - val psiManager = PsiSourceManager() - } +class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { - return psiManager.getKtFile(irFile) + logger.warn("Comment extraction is not supported for Kotlin < 1.5.20") + return null } override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { - return psiManager.findPsiElement(irElement, irFile) + logger.error("Attempted comment extraction for Kotlin < 1.5.20") + return null } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_21/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_21/Psi2Ir.kt index 256a8b3bb63..8e21191f2a0 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_21/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_21/Psi2Ir.kt @@ -1,5 +1,6 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager import org.jetbrains.kotlin.backend.jvm.ir.getKtFile @@ -7,7 +8,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -class Psi2Ir: Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { return irFile.getKtFile() } @@ -15,4 +16,4 @@ class Psi2Ir: Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return PsiSourceManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_31/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_31/Psi2Ir.kt index 256a8b3bb63..8e21191f2a0 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_31/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_5_31/Psi2Ir.kt @@ -1,5 +1,6 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager import org.jetbrains.kotlin.backend.jvm.ir.getKtFile @@ -7,7 +8,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -class Psi2Ir: Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { return irFile.getKtFile() } @@ -15,4 +16,4 @@ class Psi2Ir: Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return PsiSourceManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_10/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_10/Psi2Ir.kt index 256a8b3bb63..8e21191f2a0 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_10/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_10/Psi2Ir.kt @@ -1,5 +1,6 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager import org.jetbrains.kotlin.backend.jvm.ir.getKtFile @@ -7,7 +8,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -class Psi2Ir: Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { return irFile.getKtFile() } @@ -15,4 +16,4 @@ class Psi2Ir: Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return PsiSourceManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_20/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_20/Psi2Ir.kt index 256a8b3bb63..8e21191f2a0 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_20/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_6_20/Psi2Ir.kt @@ -1,5 +1,6 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager import org.jetbrains.kotlin.backend.jvm.ir.getKtFile @@ -7,7 +8,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -class Psi2Ir: Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { return irFile.getKtFile() } @@ -15,4 +16,4 @@ class Psi2Ir: Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return PsiSourceManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_7_0/Psi2Ir.kt b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_7_0/Psi2Ir.kt index 256a8b3bb63..8e21191f2a0 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_7_0/Psi2Ir.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/versions/v_1_7_0/Psi2Ir.kt @@ -1,5 +1,6 @@ package com.github.codeql.utils.versions +import com.github.codeql.FileLogger import com.intellij.psi.PsiElement import org.jetbrains.kotlin.backend.common.psi.PsiSourceManager import org.jetbrains.kotlin.backend.jvm.ir.getKtFile @@ -7,7 +8,7 @@ import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.psi.KtFile -class Psi2Ir: Psi2IrFacade { +class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { override fun getKtFile(irFile: IrFile): KtFile? { return irFile.getKtFile() } @@ -15,4 +16,4 @@ class Psi2Ir: Psi2IrFacade { override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? { return PsiSourceManager.findPsiElement(irElement, irFile) } -} \ No newline at end of file +} diff --git a/java/ql/consistency-queries/typeParametersInScope.ql b/java/ql/consistency-queries/typeParametersInScope.ql index f78bf2d42a4..2f1fd651278 100644 --- a/java/ql/consistency-queries/typeParametersInScope.ql +++ b/java/ql/consistency-queries/typeParametersInScope.ql @@ -12,6 +12,8 @@ Type getAMentionedType(RefType type) { result = getAMentionedType(type).(InstantiatedType).getATypeArgument() or result = getAMentionedType(type).(NestedType).getEnclosingType() + or + result = getAMentionedType(type).(Wildcard).getATypeBound().getType() } Type getATypeUsedInClass(RefType type) { diff --git a/java/ql/consistency-queries/visibility.ql b/java/ql/consistency-queries/visibility.ql index ba90d598236..1b6744cea1d 100644 --- a/java/ql/consistency-queries/visibility.ql +++ b/java/ql/consistency-queries/visibility.ql @@ -18,5 +18,6 @@ where m.getFile().isKotlinSourceFile() and // TODO: This ought to have visibility information not m.getName() = "" and - count(visibility(m)) != 1 + count(visibility(m)) != 1 and + not (count(visibility(m)) = 2 and visibility(m) = "public" and visibility(m) = "internal") // This is a reasonable result, since the JVM symbol is declared public, but Kotlin metadata flags it as internal select m, concat(visibility(m), ", ") diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/User.java b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/User.java new file mode 100644 index 00000000000..12d4b0937da --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/User.java @@ -0,0 +1,9 @@ +public class User { + + public static int test(Test1 test1, Test2 test2, Test3 test3) { + + return test1.f$main() + test2.f$mymodule() + test3.f$reservedchars___(); + + } + +} diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.expected b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.expected new file mode 100644 index 00000000000..a1fc953a254 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.expected @@ -0,0 +1,4 @@ +| User.java:3:21:3:24 | test | +| test1.kt:3:12:3:22 | f$main | +| test2.kt:3:12:3:22 | f$mymodule | +| test3.kt:3:12:3:22 | f$reservedchars___ | diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.py b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.py new file mode 100644 index 00000000000..0a41ac5b3bf --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.py @@ -0,0 +1,3 @@ +from create_database_utils import * + +run_codeql_database_create(["kotlinc test1.kt", "kotlinc test2.kt -module-name mymodule", "kotlinc test3.kt -module-name reservedchars\\\"${}/", "javac User.java -cp ." ], lang="java") diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.ql b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.ql new file mode 100644 index 00000000000..f1355df2e88 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test.ql @@ -0,0 +1,5 @@ +import java + +from Method m +where m.fromSource() +select m diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test1.kt b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test1.kt new file mode 100644 index 00000000000..c14fec0452e --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test1.kt @@ -0,0 +1,5 @@ +public class Test1 { + + internal fun f() = 1 + +} diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test2.kt b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test2.kt new file mode 100644 index 00000000000..c37d26c39fc --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test2.kt @@ -0,0 +1,5 @@ +public class Test2 { + + internal fun f() = 2 + +} diff --git a/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test3.kt b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test3.kt new file mode 100644 index 00000000000..5fcdaced80c --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/module_mangled_names/test3.kt @@ -0,0 +1,5 @@ +public class Test3 { + + internal fun f() = 3 + +} diff --git a/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/hasprops.kt b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/hasprops.kt new file mode 100644 index 00000000000..ff83b18a31a --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/hasprops.kt @@ -0,0 +1,9 @@ +class HasProps { + + var accessorsPublic = 1 + + var setterPrivate = 3 + private set + +} + diff --git a/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.expected b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.expected new file mode 100644 index 00000000000..c497684b152 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.expected @@ -0,0 +1,7 @@ +| hasprops.kt:3:3:3:25 | getAccessorsPublic | +| hasprops.kt:3:3:3:25 | setAccessorsPublic | +| hasprops.kt:5:3:6:15 | getSetterPrivate | +| hasprops.kt:6:13:6:15 | setSetterPrivate$private | +| usesprops.kt:1:1:9:1 | user | +| usesprops.kt:3:3:3:58 | useGetters | +| usesprops.kt:5:3:7:3 | useSetter | diff --git a/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.py b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.py new file mode 100644 index 00000000000..5599f459849 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.py @@ -0,0 +1,3 @@ +from create_database_utils import * + +run_codeql_database_create(["kotlinc hasprops.kt", "kotlinc usesprops.kt -cp ."], lang="java") diff --git a/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.ql b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.ql new file mode 100644 index 00000000000..f1355df2e88 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/test.ql @@ -0,0 +1,5 @@ +import java + +from Method m +where m.fromSource() +select m diff --git a/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/usesprops.kt b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/usesprops.kt new file mode 100644 index 00000000000..5157865df17 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/private_property_accessors/usesprops.kt @@ -0,0 +1,9 @@ +fun user(hp: HasProps) { + + fun useGetters() = hp.accessorsPublic + hp.setterPrivate + + fun useSetter(x: Int) { + hp.accessorsPublic = x + } + +} diff --git a/java/ql/lib/CHANGELOG.md b/java/ql/lib/CHANGELOG.md index 41b23a74d1f..150a12f68fe 100644 --- a/java/ql/lib/CHANGELOG.md +++ b/java/ql/lib/CHANGELOG.md @@ -1,3 +1,27 @@ +## 0.3.2 + +### New Features + +* The QL predicate `Expr::getUnderlyingExpr` has been added. It can be used to look through casts and not-null expressions and obtain the underlying expression to which they apply. + +### Minor Analysis Improvements + +* The JUnit5 version of `AssertNotNull` is now recognized, which removes related false positives in the nullness queries. +* Added data flow models for `java.util.Scanner`. + +## 0.3.1 + +### New Features + +* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract a type, or if an up/downgrade script is unable to provide a type. + +### Minor Analysis Improvements + +* Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance. +* Added `Modifier.isInline()`. +* Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs. +* Added additional flow sources for uses of external storage on Android. + ## 0.3.0 ### Deprecated APIs diff --git a/java/ql/lib/change-notes/2022-05-18-android-external-storage.md b/java/ql/lib/change-notes/2022-05-18-android-external-storage.md deleted file mode 100644 index b3d5fa793b3..00000000000 --- a/java/ql/lib/change-notes/2022-05-18-android-external-storage.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -Added additional flow sources for uses of external storage on Android. \ No newline at end of file diff --git a/java/ql/lib/change-notes/2022-06-13-kotlin-break-loops.md b/java/ql/lib/change-notes/2022-06-13-kotlin-break-loops.md deleted file mode 100644 index 412cf7d5ff5..00000000000 --- a/java/ql/lib/change-notes/2022-06-13-kotlin-break-loops.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs. diff --git a/java/ql/lib/change-notes/2022-06-27-isInline.md b/java/ql/lib/change-notes/2022-06-27-isInline.md deleted file mode 100644 index ad73ed8bf82..00000000000 --- a/java/ql/lib/change-notes/2022-06-27-isInline.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -Added `Modifier.isInline()`. diff --git a/java/ql/lib/change-notes/2022-08-05-asynctask-improvements.md b/java/ql/lib/change-notes/2022-08-05-asynctask-improvements.md new file mode 100644 index 00000000000..95c8438b324 --- /dev/null +++ b/java/ql/lib/change-notes/2022-08-05-asynctask-improvements.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Improved analysis of the Android class `AsyncTask` so that data can properly flow through its methods according to the life-cycle steps described here: https://developer.android.com/reference/android/os/AsyncTask#the-4-steps. \ No newline at end of file diff --git a/java/ql/lib/change-notes/released/0.3.1.md b/java/ql/lib/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..a453f034d5c --- /dev/null +++ b/java/ql/lib/change-notes/released/0.3.1.md @@ -0,0 +1,12 @@ +## 0.3.1 + +### New Features + +* Added an `ErrorType` class. An instance of this class will be used if an extractor is unable to extract a type, or if an up/downgrade script is unable to provide a type. + +### Minor Analysis Improvements + +* Added data-flow models for `java.util.Properites`. Additional results may be found where relevant data is stored in and then retrieved from a `Properties` instance. +* Added `Modifier.isInline()`. +* Removed Kotlin-specific database and QL structures for loops and `break`/`continue` statements. The Kotlin extractor was changed to reuse the Java structures for these constructs. +* Added additional flow sources for uses of external storage on Android. diff --git a/java/ql/lib/change-notes/released/0.3.2.md b/java/ql/lib/change-notes/released/0.3.2.md new file mode 100644 index 00000000000..b1d193b28b5 --- /dev/null +++ b/java/ql/lib/change-notes/released/0.3.2.md @@ -0,0 +1,10 @@ +## 0.3.2 + +### New Features + +* The QL predicate `Expr::getUnderlyingExpr` has been added. It can be used to look through casts and not-null expressions and obtain the underlying expression to which they apply. + +### Minor Analysis Improvements + +* The JUnit5 version of `AssertNotNull` is now recognized, which removes related false positives in the nullness queries. +* Added data flow models for `java.util.Scanner`. diff --git a/java/ql/lib/codeql-pack.release.yml b/java/ql/lib/codeql-pack.release.yml index 95f6e3a0ba6..18c64250f42 100644 --- a/java/ql/lib/codeql-pack.release.yml +++ b/java/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.2 diff --git a/java/ql/lib/config/semmlecode.dbscheme b/java/ql/lib/config/semmlecode.dbscheme index cf58c7d9b1f..81ccfabe82e 100755 --- a/java/ql/lib/config/semmlecode.dbscheme +++ b/java/ql/lib/config/semmlecode.dbscheme @@ -332,6 +332,14 @@ modifiers( string nodeName: string ref ); +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + classes( unique int id: @class, string nodeName: string ref, @@ -1012,13 +1020,13 @@ javadocText( @classorinterfaceorpackage = @classorinterface | @package; @classorinterfaceorcallable = @classorinterface | @callable; @boundedtype = @typevariable | @wildcard; -@reftype = @classorinterface | @array | @boundedtype; +@reftype = @classorinterface | @array | @boundedtype | @errortype; @classorarray = @class | @array; @type = @primitive | @reftype; @callable = @method | @constructor; /** A program element that has a name. */ -@element = @package | @modifier | @annotation | +@element = @package | @modifier | @annotation | @errortype | @locatableElement; @locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | diff --git a/java/ql/lib/config/semmlecode.dbscheme.stats b/java/ql/lib/config/semmlecode.dbscheme.stats index 2fc1431a73f..0490619a996 100644 --- a/java/ql/lib/config/semmlecode.dbscheme.stats +++ b/java/ql/lib/config/semmlecode.dbscheme.stats @@ -1,20 +1,20 @@ - - @diagnostic - 634718 - - - @externalDataElement - 1 - @javacompilation 8629 @kotlincompilation - 6824 + 6822 + + + @diagnostic + 624933 + + + @externalDataElement + 1 @duplication @@ -26,27 +26,31 @@ @file - 8020653 + 8017700 @folder - 1280356 + 1279884 @package - 612878 + 612652 @primitive - 12284 + 12280 @modifier - 12284 + 13644 + + + @errortype + 1 @class - 12579704 + 12579165 @kt_nullable_type @@ -54,7 +58,7 @@ @kt_notnull_type - 193427 + 194340 @kt_type_alias @@ -66,27 +70,27 @@ @fielddecl - 399940 + 399793 @field - 27583622 + 27573466 @constructor - 6889080 + 6886544 @method - 93482380 - - - @location_default - 431212495 + 93447961 @param - 101137218 + 101099980 + + + @location_default + 431053728 @exception @@ -94,19 +98,19 @@ @typevariable - 5118694 + 5116810 @wildcard - 3116261 + 3637710 @typebound - 3872463 + 4393634 @array - 1119287 + 1118875 @import @@ -114,7 +118,7 @@ @block - 846290 + 845979 @ifstmt @@ -130,7 +134,7 @@ @whilestmt - 19743 + 19739 @dostmt @@ -146,11 +150,11 @@ @synchronizedstmt - 18562 + 18706 @returnstmt - 675212 + 675118 @throwstmt @@ -182,7 +186,7 @@ @localtypedeclstmt - 4069 + 4110 @constructorinvocationstmt @@ -190,7 +194,7 @@ @superconstructorinvocationstmt - 225902 + 226569 @case @@ -202,7 +206,7 @@ @labeledstmt - 2443 + 2518 @yieldstmt @@ -214,7 +218,7 @@ @whenbranch - 238370 + 233347 @arrayaccess @@ -334,7 +338,7 @@ @andlogicalexpr - 41441 + 41715 @orlogicalexpr @@ -410,7 +414,7 @@ @instanceofexpr - 31274 + 31086 @localvariabledeclexpr @@ -418,11 +422,11 @@ @typeliteral - 147176 + 148317 @thisaccess - 952512 + 952380 @superaccess @@ -434,11 +438,11 @@ @methodaccess - 1551591 + 1551738 @unannotatedtypeaccess - 2874591 + 2867985 @arraytypeaccess @@ -486,7 +490,7 @@ @lambdaexpr - 185816 + 186985 @memberref @@ -514,7 +518,7 @@ @whenexpr - 172153 + 172129 @getclassexpr @@ -522,35 +526,35 @@ @safecastexpr - 6986 + 6943 @implicitcastexpr - 33111 + 32911 @implicitnotnullexpr - 241008 + 239473 @implicitcoerciontounitexpr - 93086 + 92272 @notinstanceofexpr - 19586 + 19583 @stmtexpr - 58107 + 57757 @stringtemplateexpr - 55964 + 55943 @notnullexpr - 21375 + 21370 @unsafecoerceexpr @@ -558,15 +562,15 @@ @valueeqexpr - 103390 + 104218 @valueneexpr - 108240 + 108225 @propertyref - 9721 + 9663 @localvar @@ -610,7 +614,7 @@ @xmlelement - 106792352 + 106753032 @javadocText @@ -618,19 +622,19 @@ @xmlattribute - 129898822 + 129850995 @xmlnamespace - 8189 + 8186 @xmlcomment - 107485764 + 107446189 @xmlcharacters - 101574013 + 101536615 @config @@ -646,15 +650,15 @@ @ktcomment - 133768 + 133719 @ktcommentsection - 59246 + 59191 @kt_property - 30317687 + 30306525 @@ -876,30 +880,30 @@
    compilation_started - 6824 + 6822 id - 6824 + 6822 compilation_args - 158338 + 158279 id - 6824 + 6822 num - 38219 + 38205 arg - 90089 + 90055 @@ -913,7 +917,7 @@ 20 21 - 2729 + 2728 23 @@ -944,7 +948,7 @@ 20 21 - 2729 + 2728 23 @@ -975,22 +979,22 @@ 1 2 - 4094 + 4093 2 3 - 2729 + 2728 3 4 - 4094 + 4093 5 6 - 27299 + 27289 @@ -1006,27 +1010,27 @@ 1 2 - 8189 + 8186 2 3 - 5459 + 5457 3 4 - 10919 + 10915 4 5 - 6824 + 6822 5 6 - 6824 + 6822 @@ -1042,22 +1046,22 @@ 1 2 - 69614 + 69588 2 3 - 2729 + 2728 4 5 - 6824 + 6822 5 6 - 10919 + 10915 @@ -1073,17 +1077,17 @@ 1 2 - 72344 + 72317 2 3 - 15014 + 15009 4 5 - 2729 + 2728 @@ -1093,19 +1097,19 @@ compilation_compiling_files - 60660 + 61130 id - 2320 + 2338 num - 17899 + 18038 file - 50716 + 51109 @@ -1119,22 +1123,22 @@ 1 2 - 331 + 334 2 3 - 662 + 668 35 36 - 662 + 668 54 55 - 662 + 668 @@ -1150,22 +1154,22 @@ 1 2 - 331 + 334 2 3 - 662 + 668 35 36 - 662 + 668 54 55 - 662 + 668 @@ -1181,17 +1185,17 @@ 2 3 - 6298 + 6346 4 5 - 10938 + 11023 6 8 - 662 + 668 @@ -1207,17 +1211,17 @@ 2 3 - 6298 + 6346 3 4 - 9944 + 10021 4 8 - 1657 + 1670 @@ -1233,12 +1237,12 @@ 1 2 - 40771 + 41087 2 3 - 9944 + 10021 @@ -1254,7 +1258,7 @@ 1 2 - 50716 + 51109 @@ -1264,19 +1268,19 @@ compilation_compiling_files_completed - 60660 + 61130 id - 2320 + 2338 num - 17899 + 18038 result - 331 + 668 @@ -1290,22 +1294,22 @@ 1 2 - 331 + 334 2 3 - 662 + 668 35 36 - 662 + 668 54 55 - 662 + 668 @@ -1321,7 +1325,12 @@ 1 2 - 2320 + 1670 + + + 2 + 3 + 668 @@ -1337,17 +1346,17 @@ 2 3 - 6298 + 6346 4 5 - 10938 + 11023 6 8 - 662 + 668 @@ -1363,7 +1372,12 @@ 1 2 - 17899 + 17704 + + + 2 + 3 + 334 @@ -1376,10 +1390,15 @@ 12 + + 2 + 3 + 334 + 7 8 - 331 + 334 @@ -1392,10 +1411,15 @@ 12 + + 1 + 2 + 334 + 54 55 - 331 + 334 @@ -1826,23 +1850,23 @@ diagnostic_for - 634718 + 624933 diagnostic - 634718 + 624933 compilation - 6824 + 6822 file_number - 8189 + 8186 file_number_diagnostic_number - 60059 + 60037 @@ -1856,7 +1880,7 @@ 1 2 - 634718 + 624933 @@ -1872,7 +1896,7 @@ 1 2 - 634718 + 624933 @@ -1888,7 +1912,7 @@ 1 2 - 634718 + 624933 @@ -1907,18 +1931,18 @@ 1364 - 86 - 87 + 84 + 85 1364 100 101 - 2729 + 2728 - 106 - 107 + 101 + 102 1364 @@ -1935,7 +1959,7 @@ 3 4 - 2729 + 2728 4 @@ -1969,8 +1993,8 @@ 1364 - 36 - 37 + 34 + 35 1364 @@ -1979,8 +2003,8 @@ 1364 - 43 - 44 + 41 + 42 1364 @@ -2005,23 +2029,23 @@ 1364 - 32 - 33 + 34 + 35 1364 - 49 - 50 + 48 + 49 1364 - 106 - 107 + 100 + 101 1364 - 130 - 131 + 128 + 129 1364 @@ -2058,7 +2082,7 @@ 5 6 - 4094 + 4093 @@ -2082,8 +2106,13 @@ 1364 - 36 - 37 + 34 + 35 + 1364 + + + 40 + 41 1364 @@ -2091,11 +2120,6 @@ 42 1364 - - 43 - 44 - 1364 - 44 45 @@ -2114,63 +2138,63 @@ 1 + 3 + 5457 + + + 3 4 - 5459 + 1364 4 5 - 5459 + 5457 5 - 7 - 2729 + 6 + 1364 7 8 - 6824 + 6822 8 9 - 4094 + 4093 9 10 - 6824 + 9551 10 - 12 - 4094 - - - 12 14 - 4094 + 5457 15 16 - 2729 + 4093 16 17 - 9554 + 6822 - 17 + 18 20 - 4094 + 5457 20 22 - 4094 + 4093 @@ -2186,22 +2210,22 @@ 1 3 - 5459 + 5457 3 4 - 5459 + 8186 4 5 - 9554 + 6822 5 6 - 39584 + 39569 @@ -2217,27 +2241,27 @@ 1 3 - 4094 + 5457 3 4 - 6824 + 8186 4 5 - 28664 + 25925 5 6 - 13649 + 13644 6 7 - 6824 + 6822 @@ -2247,11 +2271,11 @@ compilation_compiler_times - 6824 + 6822 id - 6824 + 6822 cpu_seconds @@ -2259,7 +2283,7 @@ elapsed_seconds - 6824 + 6822 @@ -2273,7 +2297,7 @@ 1 2 - 6824 + 6822 @@ -2289,7 +2313,7 @@ 1 2 - 6824 + 6822 @@ -2337,7 +2361,7 @@ 1 2 - 6824 + 6822 @@ -2353,7 +2377,7 @@ 1 2 - 6824 + 6822 @@ -2579,11 +2603,11 @@ diagnostics - 634718 + 624933 id - 634718 + 624933 generated_by @@ -2599,11 +2623,11 @@ error_message - 96913 + 95513 full_error_message - 499584 + 502129 location @@ -2621,7 +2645,7 @@ 1 2 - 634718 + 624933 @@ -2637,7 +2661,7 @@ 1 2 - 634718 + 624933 @@ -2653,7 +2677,7 @@ 1 2 - 634718 + 624933 @@ -2669,7 +2693,7 @@ 1 2 - 634718 + 624933 @@ -2685,7 +2709,7 @@ 1 2 - 634718 + 624933 @@ -2701,7 +2725,7 @@ 1 2 - 634718 + 624933 @@ -2715,8 +2739,8 @@ 12 - 465 - 466 + 458 + 459 1364 @@ -2763,8 +2787,8 @@ 12 - 71 - 72 + 70 + 71 1364 @@ -2779,8 +2803,8 @@ 12 - 366 - 367 + 368 + 369 1364 @@ -2811,8 +2835,8 @@ 12 - 465 - 466 + 458 + 459 1364 @@ -2859,8 +2883,8 @@ 12 - 71 - 72 + 70 + 71 1364 @@ -2875,8 +2899,8 @@ 12 - 366 - 367 + 368 + 369 1364 @@ -2907,8 +2931,8 @@ 12 - 465 - 466 + 458 + 459 1364 @@ -2955,8 +2979,8 @@ 12 - 71 - 72 + 70 + 71 1364 @@ -2971,8 +2995,8 @@ 12 - 366 - 367 + 368 + 369 1364 @@ -3005,67 +3029,62 @@ 1 2 - 19109 + 19102 2 3 - 6824 + 5457 3 4 - 13649 + 12280 4 5 - 4094 + 5457 5 6 - 5459 + 5457 6 7 - 6824 + 6822 7 8 - 2729 + 2728 8 9 - 9554 + 9551 9 10 - 5459 + 6822 10 11 - 6824 + 8186 - 13 - 15 - 5459 + 14 + 17 + 6822 - 16 - 19 - 8189 - - - 20 + 17 21 - 2729 + 6822 @@ -3081,7 +3100,7 @@ 1 2 - 96913 + 95513 @@ -3097,7 +3116,7 @@ 1 2 - 96913 + 95513 @@ -3113,7 +3132,7 @@ 1 2 - 96913 + 95513 @@ -3129,52 +3148,62 @@ 1 2 - 23204 + 21831 2 3 - 5459 + 5457 3 4 - 13649 + 12280 4 + 5 + 5457 + + + 5 6 - 8189 + 4093 6 7 - 6824 + 8186 7 8 - 6824 + 12280 8 9 - 16379 + 6822 9 10 - 2729 + 4093 10 11 - 6824 + 5457 11 12 - 6824 + 5457 + + + 12 + 13 + 4093 @@ -3190,7 +3219,7 @@ 1 2 - 96913 + 95513 @@ -3206,17 +3235,17 @@ 1 2 - 409495 + 418896 2 3 - 49139 + 49121 3 5 - 40949 + 34112 @@ -3232,7 +3261,7 @@ 1 2 - 499584 + 502129 @@ -3248,7 +3277,7 @@ 1 2 - 499584 + 502129 @@ -3264,7 +3293,7 @@ 1 2 - 499584 + 502129 @@ -3280,7 +3309,7 @@ 1 2 - 499584 + 502129 @@ -3296,7 +3325,7 @@ 1 2 - 499584 + 502129 @@ -3310,8 +3339,8 @@ 12 - 465 - 466 + 458 + 459 1364 @@ -3374,8 +3403,8 @@ 12 - 71 - 72 + 70 + 71 1364 @@ -3390,8 +3419,8 @@ 12 - 366 - 367 + 368 + 369 1364 @@ -4848,31 +4877,31 @@ locations_default - 431212495 + 431053728 id - 431212495 + 431053728 file - 8020653 + 8017700 beginLine - 2802314 + 2801282 beginColumn - 176083 + 176018 endLine - 2803679 + 2802647 endColumn - 621068 + 620839 @@ -4886,7 +4915,7 @@ 1 2 - 431212495 + 431053728 @@ -4902,7 +4931,7 @@ 1 2 - 431212495 + 431053728 @@ -4918,7 +4947,7 @@ 1 2 - 431212495 + 431053728 @@ -4934,7 +4963,7 @@ 1 2 - 431212495 + 431053728 @@ -4950,7 +4979,7 @@ 1 2 - 431212495 + 431053728 @@ -4966,17 +4995,17 @@ 1 2 - 7179822 + 7177178 2 20 - 604688 + 604465 20 3605 - 236142 + 236055 @@ -4992,17 +5021,17 @@ 1 2 - 7179822 + 7177178 2 15 - 601958 + 601736 15 1830 - 238872 + 238784 @@ -5018,17 +5047,17 @@ 1 2 - 7179822 + 7177178 2 7 - 629258 + 629026 7 105 - 211572 + 211494 @@ -5044,17 +5073,17 @@ 1 2 - 7179822 + 7177178 2 18 - 601958 + 601736 18 1834 - 238872 + 238784 @@ -5070,17 +5099,17 @@ 1 2 - 7179822 + 7177178 2 15 - 604688 + 604465 15 205 - 236142 + 236055 @@ -5096,67 +5125,67 @@ 1 14 - 222492 + 222410 14 125 - 215667 + 215588 125 142 - 215667 + 215588 142 152 - 223857 + 223775 152 159 - 249792 + 249700 159 165 - 257982 + 257887 165 170 - 226587 + 226504 170 174 - 217032 + 216952 174 179 - 229317 + 229233 179 185 - 214302 + 214223 185 194 - 222492 + 222410 194 215 - 215667 + 215588 215 5877 - 91454 + 91420 @@ -5172,67 +5201,67 @@ 1 7 - 229317 + 229233 7 65 - 212937 + 212859 65 73 - 217032 + 216952 73 78 - 207477 + 207401 78 81 - 191097 + 191027 81 84 - 249792 + 249700 84 86 - 215667 + 215588 86 88 - 257982 + 257887 88 90 - 222492 + 222410 90 93 - 248427 + 248335 93 97 - 218397 + 218317 97 106 - 214302 + 214223 106 5877 - 117388 + 117345 @@ -5248,62 +5277,62 @@ 1 5 - 222492 + 222410 5 17 - 196557 + 196485 17 19 - 167893 + 167831 19 20 - 214302 + 214223 20 21 - 268902 + 268803 21 22 - 283916 + 283812 22 23 - 330326 + 330204 23 24 - 304391 + 304279 24 25 - 236142 + 236055 25 26 - 170623 + 170560 26 28 - 212937 + 212859 28 45 - 193827 + 193756 @@ -5319,32 +5348,32 @@ 1 2 - 904985 + 904652 2 3 - 896795 + 896465 3 4 - 483204 + 483026 4 5 - 211572 + 211494 5 11 - 221127 + 221046 11 97 - 84629 + 84597 @@ -5360,72 +5389,72 @@ 1 13 - 219762 + 219681 13 60 - 210207 + 210130 60 64 - 223857 + 223775 64 66 - 210207 + 210130 66 68 - 259347 + 259251 68 69 - 131038 + 130990 69 70 - 155608 + 155551 70 71 - 135133 + 135083 71 73 - 236142 + 236055 73 75 - 212937 + 212859 75 78 - 234777 + 234691 78 82 - 252522 + 252429 82 89 - 214302 + 214223 89 104 - 106468 + 106429 @@ -5441,67 +5470,67 @@ 1 11 - 15014 + 15009 15 34 - 15014 + 15009 36 58 - 13649 + 13644 63 88 - 13649 + 13644 89 132 - 13649 + 13644 141 196 - 15014 + 15009 210 285 - 13649 + 13644 316 468 - 13649 + 13644 496 853 - 13649 + 13644 899 1420 - 13649 + 13644 1472 2256 - 13649 + 13644 2300 2526 - 13649 + 13644 2589 226687 - 8189 + 8186 @@ -5517,72 +5546,72 @@ 1 9 - 9554 + 9551 9 11 - 13649 + 13644 12 16 - 8189 + 8186 16 19 - 13649 + 13644 19 31 - 13649 + 13644 35 73 - 13649 + 13644 73 83 - 13649 + 13644 85 104 - 15014 + 15009 104 110 - 10919 + 10915 110 114 - 15014 + 15009 115 119 - 13649 + 13644 119 121 - 12284 + 12280 121 126 - 13649 + 13644 126 5877 - 9554 + 9551 @@ -5598,67 +5627,67 @@ 1 10 - 15014 + 15009 10 29 - 13649 + 13644 29 43 - 13649 + 13644 45 58 - 13649 + 13644 58 85 - 13649 + 13644 86 106 - 13649 + 13644 108 167 - 13649 + 13644 171 227 - 13649 + 13644 231 379 - 13649 + 13644 383 650 - 13649 + 13644 651 891 - 13649 + 13644 940 1086 - 13649 + 13644 1093 2051 - 10919 + 10915 @@ -5674,67 +5703,67 @@ 1 10 - 15014 + 15009 10 30 - 13649 + 13644 30 43 - 13649 + 13644 46 59 - 13649 + 13644 60 87 - 13649 + 13644 87 109 - 13649 + 13644 115 173 - 13649 + 13644 174 226 - 13649 + 13644 230 380 - 13649 + 13644 383 650 - 13649 + 13644 653 892 - 13649 + 13644 940 1084 - 13649 + 13644 1092 2051 - 10919 + 10915 @@ -5750,67 +5779,67 @@ 1 8 - 15014 + 15009 8 17 - 13649 + 13644 18 25 - 10919 + 10915 25 30 - 13649 + 13644 30 36 - 13649 + 13644 36 44 - 13649 + 13644 45 56 - 12284 + 12280 57 64 - 13649 + 13644 64 73 - 13649 + 13644 73 86 - 13649 + 13644 86 106 - 13649 + 13644 107 129 - 13649 + 13644 129 197 - 13649 + 13644 392 @@ -5831,67 +5860,67 @@ 1 14 - 221127 + 221046 14 124 - 215667 + 215588 124 143 - 218397 + 218317 143 152 - 236142 + 236055 152 159 - 223857 + 223775 159 165 - 257982 + 257887 165 170 - 240237 + 240148 170 174 - 222492 + 222410 174 179 - 222492 + 222410 179 185 - 211572 + 211494 185 194 - 217032 + 216952 194 217 - 212937 + 212859 217 5877 - 103738 + 103700 @@ -5907,67 +5936,67 @@ 1 7 - 232047 + 231962 7 66 - 225222 + 225139 66 74 - 227952 + 227868 74 80 - 251157 + 251064 80 83 - 241602 + 241513 83 85 - 185637 + 185569 85 87 - 247062 + 246971 87 89 - 247062 + 246971 89 91 - 188367 + 188298 91 94 - 248427 + 248335 94 99 - 226587 + 226504 99 127 - 212937 + 212859 127 5877 - 69614 + 69588 @@ -5983,32 +6012,32 @@ 1 2 - 711157 + 710895 2 3 - 989614 + 989249 3 4 - 644273 + 644035 4 6 - 237507 + 237419 6 18 - 212937 + 212859 18 22 - 8189 + 8186 @@ -6024,62 +6053,62 @@ 1 5 - 219762 + 219681 5 17 - 199287 + 199214 17 19 - 163798 + 163737 19 20 - 192462 + 192392 20 21 - 281186 + 281083 21 22 - 272997 + 272896 22 23 - 330326 + 330204 23 24 - 307121 + 307008 24 25 - 225222 + 225139 25 26 - 184273 + 184205 26 28 - 223857 + 223775 28 44 - 203382 + 203307 @@ -6095,72 +6124,72 @@ 1 13 - 222492 + 222410 13 61 - 251157 + 251064 61 64 - 173353 + 173289 64 66 - 218397 + 218317 66 68 - 251157 + 251064 68 69 - 137863 + 137812 69 70 - 137863 + 137812 70 71 - 158338 + 158279 71 73 - 232047 + 231962 73 75 - 192462 + 192392 75 77 - 184273 + 184205 77 80 - 211572 + 211494 80 85 - 227952 + 227868 85 119 - 204747 + 204672 @@ -6176,57 +6205,57 @@ 1 2 - 146053 + 145999 2 3 - 64154 + 64130 3 5 - 50504 + 50485 5 13 - 50504 + 50485 13 53 - 47774 + 47756 53 146 - 47774 + 47756 146 351 - 47774 + 47756 357 997 - 47774 + 47756 1053 2396 - 47774 + 47756 2407 4957 - 47774 + 47756 5022 5934 - 23204 + 23196 @@ -6242,57 +6271,57 @@ 1 2 - 151513 + 151457 2 3 - 61424 + 61401 3 5 - 53234 + 53214 5 13 - 50504 + 50485 13 42 - 47774 + 47756 42 77 - 47774 + 47756 77 103 - 51869 + 51850 103 116 - 47774 + 47756 116 144 - 49139 + 49121 144 181 - 47774 + 47756 181 5877 - 12284 + 12280 @@ -6308,57 +6337,57 @@ 1 2 - 154243 + 154186 2 3 - 62789 + 62766 3 5 - 49139 + 49121 5 13 - 49139 + 49121 13 52 - 49139 + 49121 52 115 - 47774 + 47756 123 271 - 47774 + 47756 277 658 - 47774 + 47756 669 1200 - 47774 + 47756 1219 1635 - 47774 + 47756 1639 1722 - 17744 + 17738 @@ -6374,47 +6403,47 @@ 1 2 - 195192 + 195121 2 3 - 75074 + 75046 3 6 - 54599 + 54579 6 14 - 54599 + 54579 14 26 - 47774 + 47756 26 36 - 49139 + 49121 36 47 - 49139 + 49121 47 55 - 47774 + 47756 55 68 - 47774 + 47756 @@ -6430,57 +6459,57 @@ 1 2 - 154243 + 154186 2 3 - 61424 + 61401 3 5 - 49139 + 49121 5 13 - 49139 + 49121 13 53 - 50504 + 50485 53 115 - 47774 + 47756 123 271 - 47774 + 47756 280 656 - 47774 + 47756 669 1200 - 47774 + 47756 1217 1638 - 47774 + 47756 1640 1722 - 17744 + 17738 @@ -6490,15 +6519,15 @@ hasLocation - 316110113 + 316902470 locatableid - 315965424 + 316759199 id - 11558695 + 11554439 @@ -6512,12 +6541,12 @@ 1 2 - 315820736 + 316615929 2 3 - 144688 + 143270 @@ -6533,62 +6562,62 @@ 1 2 - 2097982 + 2097209 2 3 - 1008724 + 1008352 3 4 - 719347 + 717717 4 6 - 977329 + 978334 6 7 - 773946 + 773661 7 9 - 1019643 + 1019268 9 12 - 850385 + 848708 12 16 - 925460 + 926483 16 23 - 883145 + 882820 23 39 - 888605 + 888278 39 89 - 874955 + 873268 89 - 9987 - 539169 + 9991 + 540335 @@ -6598,23 +6627,23 @@ numlines - 215080728 + 215001538 element_id - 215080728 + 215001538 num_lines - 432700 + 432541 num_code - 434065 + 433905 num_comment - 1345875 + 1345379 @@ -6628,7 +6657,7 @@ 1 2 - 215080728 + 215001538 @@ -6644,7 +6673,7 @@ 1 2 - 215080728 + 215001538 @@ -6660,7 +6689,7 @@ 1 2 - 215080728 + 215001538 @@ -6676,37 +6705,37 @@ 1 2 - 232047 + 231962 2 3 - 53234 + 53214 3 4 - 45044 + 45027 4 7 - 34124 + 34112 7 14 - 32759 + 32747 15 839 - 32759 + 32747 3519 149603 - 2729 + 2728 @@ -6722,12 +6751,12 @@ 1 2 - 423145 + 422989 2 3 - 9554 + 9551 @@ -6743,27 +6772,27 @@ 1 2 - 274362 + 274261 2 3 - 69614 + 69588 3 4 - 36854 + 36841 4 6 - 34124 + 34112 6 987 - 17744 + 17738 @@ -6779,37 +6808,37 @@ 1 2 - 232047 + 231962 2 3 - 53234 + 53214 3 4 - 45044 + 45027 4 7 - 34124 + 34112 7 14 - 32759 + 32747 15 468 - 32759 + 32747 495 78746 - 4094 + 4093 @@ -6825,7 +6854,7 @@ 1 2 - 432700 + 432541 7 @@ -6846,27 +6875,27 @@ 1 2 - 274362 + 274261 2 3 - 69614 + 69588 3 4 - 36854 + 36841 4 6 - 34124 + 34112 6 987 - 19109 + 19102 @@ -6882,77 +6911,77 @@ 1 7 - 109198 + 109158 7 49 - 101008 + 100971 49 71 - 105103 + 105065 71 78 - 107833 + 107794 78 83 - 103738 + 103700 83 87 - 116023 + 115981 87 89 - 99643 + 99607 89 91 - 95548 + 95513 91 92 - 57329 + 57308 92 93 - 68249 + 68224 93 94 - 75074 + 75046 94 95 - 69614 + 69588 95 97 - 109198 + 109158 97 119 - 101008 + 100971 119 75115 - 27299 + 27289 @@ -6968,22 +6997,22 @@ 1 2 - 1104273 + 1103866 2 3 - 114658 + 114616 3 6 - 102373 + 102336 6 120 - 24569 + 24560 @@ -6999,22 +7028,22 @@ 1 2 - 1104273 + 1103866 2 3 - 114658 + 114616 3 6 - 101008 + 100971 6 121 - 25934 + 25925 @@ -7024,15 +7053,15 @@ files - 8020653 + 8017700 id - 8020653 + 8017700 name - 8020653 + 8017700 @@ -7046,7 +7075,7 @@ 1 2 - 8020653 + 8017700 @@ -7062,7 +7091,7 @@ 1 2 - 8020653 + 8017700 @@ -7072,15 +7101,15 @@ folders - 1280356 + 1279884 id - 1280356 + 1279884 name - 1280356 + 1279884 @@ -7094,7 +7123,7 @@ 1 2 - 1280356 + 1279884 @@ -7110,7 +7139,7 @@ 1 2 - 1280356 + 1279884 @@ -7120,15 +7149,15 @@ containerparent - 9298279 + 9294856 parent - 1321305 + 1320819 child - 9298279 + 9294856 @@ -7142,37 +7171,37 @@ 1 2 - 713887 + 713624 2 3 - 140593 + 140541 3 4 - 79169 + 79139 4 7 - 113293 + 113252 7 14 - 111928 + 111887 14 29 - 102373 + 102336 29 194 - 60059 + 60037 @@ -7188,7 +7217,7 @@ 1 2 - 9298279 + 9294856 @@ -7198,15 +7227,15 @@ cupackage - 7160712 + 7158076 id - 7160712 + 7158076 packageid - 611513 + 611288 @@ -7220,7 +7249,7 @@ 1 2 - 7160712 + 7158076 @@ -7236,52 +7265,52 @@ 1 2 - 148783 + 148728 2 3 - 80534 + 80504 3 4 - 55964 + 55943 4 5 - 42314 + 42298 5 7 - 46409 + 46392 7 10 - 50504 + 50485 10 15 - 50504 + 50485 15 21 - 49139 + 49121 21 36 - 47774 + 47756 39 187 - 39584 + 39569 @@ -8043,15 +8072,15 @@ packages - 612878 + 612652 id - 612878 + 612652 nodeName - 612878 + 612652 @@ -8065,7 +8094,7 @@ 1 2 - 612878 + 612652 @@ -8081,7 +8110,7 @@ 1 2 - 612878 + 612652 @@ -8091,15 +8120,15 @@ primitives - 12284 + 12280 id - 12284 + 12280 nodeName - 12284 + 12280 @@ -8113,7 +8142,7 @@ 1 2 - 12284 + 12280 @@ -8129,7 +8158,7 @@ 1 2 - 12284 + 12280 @@ -8139,15 +8168,15 @@ modifiers - 12284 + 13644 id - 12284 + 13644 nodeName - 12284 + 13644 @@ -8161,7 +8190,7 @@ 1 2 - 12284 + 13644 @@ -8177,7 +8206,7 @@ 1 2 - 12284 + 13644 @@ -8186,24 +8215,35 @@ - classes - 12579704 + error_type + 1 id - 12579704 + 1 + + + + + + classes + 12579165 + + + id + 12579165 nodeName - 6868605 + 6871534 parentid - 443620 + 443456 sourceid - 4519466 + 4517802 @@ -8217,7 +8257,7 @@ 1 2 - 12579704 + 12579165 @@ -8233,7 +8273,7 @@ 1 2 - 12579704 + 12579165 @@ -8249,7 +8289,7 @@ 1 2 - 12579704 + 12579165 @@ -8265,17 +8305,17 @@ 1 2 - 5735668 + 5740378 2 3 - 746646 + 745007 3 236 - 386290 + 386148 @@ -8291,17 +8331,17 @@ 1 2 - 6252997 + 6256153 2 3 - 547359 + 547157 3 52 - 68249 + 68224 @@ -8317,17 +8357,17 @@ 1 2 - 6229792 + 6232956 2 3 - 536439 + 536241 3 160 - 102373 + 102336 @@ -8343,57 +8383,57 @@ 1 2 - 107833 + 107794 2 3 - 54599 + 54579 3 4 - 32759 + 32747 4 5 - 30029 + 30018 5 7 - 34124 + 34112 7 11 - 40949 + 40934 11 17 - 34124 + 34112 17 23 - 34124 + 34112 23 40 - 36854 + 36841 40 707 - 34124 + 34112 1013 - 1412 - 4094 + 1414 + 4093 @@ -8409,52 +8449,52 @@ 1 2 - 107833 + 107794 2 3 - 54599 + 54579 3 4 - 35489 + 35476 4 5 - 34124 + 34112 5 7 - 38219 + 38205 7 11 - 40949 + 40934 11 17 - 36854 + 36841 17 23 - 34124 + 34112 23 40 - 35489 + 35476 40 - 828 - 25934 + 830 + 25925 @@ -8470,52 +8510,52 @@ 1 2 - 118753 + 118709 2 3 - 64154 + 64130 3 4 - 36854 + 36841 4 5 - 34124 + 34112 5 7 - 35489 + 35476 7 11 - 40949 + 40934 11 17 - 34124 + 34112 17 26 - 34124 + 34112 26 56 - 34124 + 34112 64 138 - 10919 + 10915 @@ -8531,17 +8571,17 @@ 1 2 - 4052641 + 4051149 2 11 - 342611 + 342485 11 1358 - 124213 + 124167 @@ -8557,17 +8597,17 @@ 1 2 - 4052641 + 4051149 2 6 - 360356 + 360223 6 783 - 106468 + 106429 @@ -8583,7 +8623,7 @@ 1 2 - 4519466 + 4517802 @@ -8593,26 +8633,26 @@ file_class - 15014 + 15009 id - 15014 + 15009 class_object - 122848 + 122803 id - 122848 + 122803 instance - 122848 + 122803 @@ -8626,7 +8666,7 @@ 1 2 - 122848 + 122803 @@ -8642,7 +8682,7 @@ 1 2 - 122848 + 122803 @@ -8652,19 +8692,19 @@ type_companion_object - 218397 + 218317 id - 218397 + 218317 instance - 218397 + 218317 companion_object - 218397 + 218317 @@ -8678,7 +8718,7 @@ 1 2 - 218397 + 218317 @@ -8694,7 +8734,7 @@ 1 2 - 218397 + 218317 @@ -8710,7 +8750,7 @@ 1 2 - 218397 + 218317 @@ -8726,7 +8766,7 @@ 1 2 - 218397 + 218317 @@ -8742,7 +8782,7 @@ 1 2 - 218397 + 218317 @@ -8758,7 +8798,7 @@ 1 2 - 218397 + 218317 @@ -8816,15 +8856,15 @@ kt_notnull_types - 193427 + 194340 id - 193427 + 194340 classid - 193427 + 194340 @@ -8838,7 +8878,7 @@ 1 2 - 193427 + 194340 @@ -8854,7 +8894,7 @@ 1 2 - 193427 + 194340 @@ -9437,15 +9477,15 @@ fielddecls - 399940 + 399793 id - 399940 + 399793 parentid - 60059 + 60037 @@ -9459,7 +9499,7 @@ 1 2 - 399940 + 399793 @@ -9475,32 +9515,32 @@ 1 2 - 30029 + 30018 2 3 - 13649 + 13644 3 4 - 5459 + 5457 4 5 - 2729 + 2728 6 16 - 5459 + 5457 40 159 - 2729 + 2728 @@ -9510,15 +9550,15 @@ fieldDeclaredIn - 399940 + 399793 fieldId - 399940 + 399793 fieldDeclId - 399940 + 399793 pos @@ -9536,7 +9576,7 @@ 1 2 - 399940 + 399793 @@ -9552,7 +9592,7 @@ 1 2 - 399940 + 399793 @@ -9568,7 +9608,7 @@ 1 2 - 399940 + 399793 @@ -9584,7 +9624,7 @@ 1 2 - 399940 + 399793 @@ -9626,27 +9666,27 @@ fields - 27583622 + 27573466 id - 27583622 + 27573466 nodeName - 10929437 + 10925412 typeid - 2735430 + 2734423 parentid - 3861543 + 3860121 sourceid - 27583622 + 27573466 @@ -9660,7 +9700,7 @@ 1 2 - 27583622 + 27573466 @@ -9676,7 +9716,7 @@ 1 2 - 27583622 + 27573466 @@ -9692,7 +9732,7 @@ 1 2 - 27583622 + 27573466 @@ -9708,7 +9748,7 @@ 1 2 - 27583622 + 27573466 @@ -9724,22 +9764,22 @@ 1 2 - 8083442 + 8080466 2 3 - 1437329 + 1436800 3 11 - 763026 + 762745 11 828 - 645638 + 645400 @@ -9755,17 +9795,17 @@ 1 2 - 9901603 + 9897957 2 4 - 853115 + 852801 4 160 - 174718 + 174653 @@ -9781,22 +9821,22 @@ 1 2 - 8083442 + 8080466 2 3 - 1437329 + 1436800 3 11 - 763026 + 762745 11 828 - 645638 + 645400 @@ -9812,22 +9852,22 @@ 1 2 - 8083442 + 8080466 2 3 - 1437329 + 1436800 3 11 - 763026 + 762745 11 828 - 645638 + 645400 @@ -9843,32 +9883,32 @@ 1 2 - 1737626 + 1736986 2 3 - 339881 + 339756 3 4 - 176083 + 176018 4 7 - 208842 + 208765 7 24 - 206112 + 206036 24 7479 - 66884 + 66859 @@ -9884,27 +9924,27 @@ 1 2 - 1900059 + 1899359 2 3 - 303026 + 302915 3 4 - 184273 + 184205 4 9 - 212937 + 212859 9 2335 - 135133 + 135083 @@ -9920,17 +9960,17 @@ 1 2 - 2332759 + 2331900 2 4 - 229317 + 229233 4 1392 - 173353 + 173289 @@ -9946,32 +9986,32 @@ 1 2 - 1737626 + 1736986 2 3 - 339881 + 339756 3 4 - 176083 + 176018 4 7 - 208842 + 208765 7 24 - 206112 + 206036 24 7479 - 66884 + 66859 @@ -9987,37 +10027,37 @@ 1 2 - 1942374 + 1941658 2 3 - 485934 + 485755 3 4 - 304391 + 304279 4 6 - 342611 + 342485 6 10 - 301661 + 301550 10 27 - 292106 + 291999 27 1610 - 192462 + 192392 @@ -10033,37 +10073,37 @@ 1 2 - 1942374 + 1941658 2 3 - 485934 + 485755 3 4 - 304391 + 304279 4 6 - 342611 + 342485 6 10 - 301661 + 301550 10 27 - 292106 + 291999 27 1610 - 192462 + 192392 @@ -10079,27 +10119,27 @@ 1 2 - 2506112 + 2505190 2 3 - 625163 + 624933 3 4 - 244332 + 244242 4 7 - 339881 + 339756 7 76 - 146053 + 145999 @@ -10115,37 +10155,37 @@ 1 2 - 1942374 + 1941658 2 3 - 485934 + 485755 3 4 - 304391 + 304279 4 6 - 342611 + 342485 6 10 - 301661 + 301550 10 27 - 292106 + 291999 27 1610 - 192462 + 192392 @@ -10161,7 +10201,7 @@ 1 2 - 27583622 + 27573466 @@ -10177,7 +10217,7 @@ 1 2 - 27583622 + 27573466 @@ -10193,7 +10233,7 @@ 1 2 - 27583622 + 27573466 @@ -10209,7 +10249,7 @@ 1 2 - 27583622 + 27573466 @@ -10219,11 +10259,11 @@ fieldsKotlinType - 27583622 + 27573466 id - 27583622 + 27573466 kttypeid @@ -10241,7 +10281,7 @@ 1 2 - 27583622 + 27573466 @@ -10267,31 +10307,31 @@ constrs - 6889080 + 6886544 id - 6889080 + 6886544 nodeName - 3756439 + 3755056 signature - 5887181 + 5885013 typeid - 2729 + 2728 parentid - 4849792 + 4848007 sourceid - 5069555 + 5067688 @@ -10305,7 +10345,7 @@ 1 2 - 6889080 + 6886544 @@ -10321,7 +10361,7 @@ 1 2 - 6889080 + 6886544 @@ -10337,7 +10377,7 @@ 1 2 - 6889080 + 6886544 @@ -10353,7 +10393,7 @@ 1 2 - 6889080 + 6886544 @@ -10369,7 +10409,7 @@ 1 2 - 6889080 + 6886544 @@ -10385,22 +10425,22 @@ 1 2 - 2366884 + 2366012 2 3 - 839465 + 839156 3 5 - 346706 + 346578 5 42 - 203382 + 203307 @@ -10416,22 +10456,22 @@ 1 2 - 2635786 + 2634816 2 3 - 685222 + 684970 3 5 - 303026 + 302915 5 19 - 132403 + 132354 @@ -10447,7 +10487,7 @@ 1 2 - 3756439 + 3755056 @@ -10463,17 +10503,17 @@ 1 2 - 3295074 + 3293861 2 3 - 316676 + 316559 3 42 - 144688 + 144635 @@ -10489,22 +10529,22 @@ 1 2 - 2458338 + 2457433 2 3 - 831276 + 830969 3 5 - 319406 + 319288 5 38 - 147418 + 147364 @@ -10520,12 +10560,12 @@ 1 2 - 5474955 + 5472940 2 42 - 412225 + 412073 @@ -10541,7 +10581,7 @@ 1 2 - 5887181 + 5885013 @@ -10557,7 +10597,7 @@ 1 2 - 5887181 + 5885013 @@ -10573,12 +10613,12 @@ 1 2 - 5474955 + 5472940 2 42 - 412225 + 412073 @@ -10594,12 +10634,12 @@ 1 2 - 5604629 + 5602565 2 32 - 282551 + 282447 @@ -10720,22 +10760,22 @@ 1 2 - 3688190 + 3686832 2 3 - 739822 + 739549 3 6 - 367181 + 367045 6 19 - 54599 + 54579 @@ -10751,7 +10791,7 @@ 1 2 - 4849792 + 4848007 @@ -10767,22 +10807,22 @@ 1 2 - 3688190 + 3686832 2 3 - 739822 + 739549 3 6 - 367181 + 367045 6 19 - 54599 + 54579 @@ -10798,7 +10838,7 @@ 1 2 - 4849792 + 4848007 @@ -10814,22 +10854,22 @@ 1 2 - 3688190 + 3686832 2 3 - 739822 + 739549 3 6 - 367181 + 367045 6 19 - 54599 + 54579 @@ -10845,12 +10885,12 @@ 1 2 - 4756973 + 4755222 2 259 - 312581 + 312466 @@ -10866,12 +10906,12 @@ 1 2 - 4756973 + 4755222 2 212 - 312581 + 312466 @@ -10887,12 +10927,12 @@ 1 2 - 4756973 + 4755222 2 212 - 312581 + 312466 @@ -10908,7 +10948,7 @@ 1 2 - 5069555 + 5067688 @@ -10924,12 +10964,12 @@ 1 2 - 4756973 + 4755222 2 259 - 312581 + 312466 @@ -10939,11 +10979,11 @@ constrsKotlinType - 6889080 + 6886544 id - 6889080 + 6886544 kttypeid @@ -10961,7 +11001,7 @@ 1 2 - 6889080 + 6886544 @@ -10987,31 +11027,31 @@ methods - 93482380 + 93447961 id - 93482380 + 93447961 nodeName - 20395609 + 20385371 signature - 29871337 + 29857610 typeid - 11611929 + 11610383 parentid - 11303442 + 11299281 sourceid - 58623387 + 58601802 @@ -11025,7 +11065,7 @@ 1 2 - 93482380 + 93447961 @@ -11041,7 +11081,7 @@ 1 2 - 93482380 + 93447961 @@ -11057,7 +11097,7 @@ 1 2 - 93482380 + 93447961 @@ -11073,7 +11113,7 @@ 1 2 - 93482380 + 93447961 @@ -11089,7 +11129,7 @@ 1 2 - 93482380 + 93447961 @@ -11105,32 +11145,32 @@ 1 2 - 11927241 + 11921485 2 3 - 3791929 + 3790532 3 4 - 1317210 + 1316725 4 7 - 1789495 + 1788836 7 - 259 - 1530148 + 271 + 1529585 - 270 + 276 2134 - 39584 + 38205 @@ -11146,17 +11186,17 @@ 1 2 - 16912167 + 16903211 2 3 - 2122552 + 2121770 3 361 - 1360890 + 1360389 @@ -11172,22 +11212,22 @@ 1 2 - 17141484 + 17133809 2 3 - 1683026 + 1682407 3 - 52 - 1530148 + 53 + 1529585 - 52 + 53 845 - 40949 + 39569 @@ -11203,27 +11243,27 @@ 1 2 - 12654778 + 12648754 2 3 - 3558516 + 3557206 3 4 - 1250326 + 1249866 4 7 - 1575192 + 1574613 7 1278 - 1356795 + 1354931 @@ -11239,27 +11279,27 @@ 1 2 - 12309437 + 12302175 2 3 - 3921602 + 3921523 3 4 - 1298100 + 1297623 4 7 - 1654362 + 1653753 7 928 - 1212106 + 1210296 @@ -11275,22 +11315,22 @@ 1 2 - 20522553 + 20513632 2 3 - 4879822 + 4878025 3 5 - 2368249 + 2367377 5 1275 - 2100712 + 2098574 @@ -11306,7 +11346,7 @@ 1 2 - 29871337 + 29857610 @@ -11322,17 +11362,17 @@ 1 2 - 26823325 + 26812084 2 5 - 2390089 + 2389209 5 843 - 657922 + 656316 @@ -11348,22 +11388,22 @@ 1 2 - 20526648 + 20517726 2 3 - 4878457 + 4876661 3 5 - 2366884 + 2366012 5 1275 - 2099347 + 2097209 @@ -11379,22 +11419,22 @@ 1 2 - 21147716 + 21137201 2 3 - 4997211 + 4996735 3 6 - 2537507 + 2536573 6 923 - 1188902 + 1187099 @@ -11410,32 +11450,32 @@ 1 2 - 5705638 + 5703537 2 3 - 2455608 + 2457433 3 4 - 1087893 + 1087492 4 6 - 924095 + 925119 6 14 - 877685 + 875997 14 11682 - 561008 + 560802 @@ -11451,22 +11491,22 @@ 1 2 - 7632997 + 7632916 2 3 - 2222196 + 2221377 3 6 - 1063323 + 1064296 6 3956 - 693412 + 691792 @@ -11482,27 +11522,27 @@ 1 2 - 7390030 + 7388673 2 3 - 2276795 + 2278686 3 5 - 879050 + 878726 5 17 - 884510 + 882820 17 5707 - 181543 + 181476 @@ -11518,27 +11558,27 @@ 1 2 - 6811276 + 6808768 2 3 - 2467893 + 2471078 3 4 - 1004629 + 1004259 4 9 - 917270 + 915567 9 3421 - 410860 + 410709 @@ -11554,32 +11594,32 @@ 1 2 - 6202493 + 6200209 2 3 - 2268605 + 2270499 3 4 - 984154 + 983792 4 6 - 909080 + 910109 6 16 - 892700 + 891007 16 8870 - 354896 + 354765 @@ -11595,52 +11635,52 @@ 1 2 - 3200890 + 3199711 2 3 - 1298100 + 1297623 3 4 - 1027833 + 1027455 4 5 - 748011 + 747736 5 7 - 925460 + 925119 7 10 - 959584 + 959231 10 13 - 997804 + 997436 13 19 - 909080 + 908745 19 38 - 910445 + 910109 38 319 - 326231 + 326111 @@ -11656,52 +11696,52 @@ 1 2 - 3254124 + 3252926 2 3 - 1318575 + 1318090 3 4 - 1108368 + 1107959 4 5 - 788961 + 788670 5 7 - 888605 + 888278 7 10 - 1021008 + 1020633 10 13 - 941839 + 942857 13 17 - 876320 + 874633 17 35 - 850385 + 850072 35 290 - 255252 + 255158 @@ -11717,52 +11757,52 @@ 1 2 - 3200890 + 3199711 2 3 - 1298100 + 1297623 3 4 - 1027833 + 1027455 4 5 - 748011 + 747736 5 7 - 925460 + 925119 7 10 - 959584 + 959231 10 13 - 997804 + 997436 13 19 - 909080 + 908745 19 38 - 910445 + 910109 38 319 - 326231 + 326111 @@ -11778,47 +11818,47 @@ 1 2 - 3901127 + 3899691 2 3 - 1624332 + 1623734 3 4 - 1329495 + 1329006 4 5 - 790326 + 790035 5 6 - 636083 + 635848 6 7 - 502314 + 502129 7 9 - 1034658 + 1034277 9 13 - 881780 + 881455 13 78 - 603323 + 603101 @@ -11834,52 +11874,52 @@ 1 2 - 3200890 + 3199711 2 3 - 1298100 + 1297623 3 4 - 1027833 + 1027455 4 5 - 748011 + 747736 5 7 - 925460 + 925119 7 10 - 959584 + 959231 10 13 - 997804 + 997436 13 19 - 909080 + 908745 19 38 - 910445 + 910109 38 319 - 326231 + 326111 @@ -11895,12 +11935,12 @@ 1 2 - 54259529 + 54239551 2 349 - 4363857 + 4362251 @@ -11916,7 +11956,7 @@ 1 2 - 58623387 + 58601802 @@ -11932,12 +11972,12 @@ 1 2 - 58339470 + 58317990 2 347 - 283916 + 283812 @@ -11953,12 +11993,12 @@ 1 2 - 56923980 + 56903021 2 259 - 1699406 + 1698780 @@ -11974,12 +12014,12 @@ 1 2 - 54259529 + 54239551 2 349 - 4363857 + 4362251 @@ -11989,11 +12029,11 @@ methodsKotlinType - 93482380 + 93447961 id - 93482380 + 93447961 kttypeid @@ -12011,7 +12051,7 @@ 1 2 - 93482380 + 93447961 @@ -12037,27 +12077,27 @@ params - 101137218 + 101099980 id - 101137218 + 101099980 typeid - 11278873 + 11198309 pos - 30029 + 30018 parentid - 56339766 + 56319023 sourceid - 61326058 + 61303478 @@ -12071,7 +12111,7 @@ 1 2 - 101137218 + 101099980 @@ -12087,7 +12127,7 @@ 1 2 - 101137218 + 101099980 @@ -12103,7 +12143,7 @@ 1 2 - 101137218 + 101099980 @@ -12119,7 +12159,7 @@ 1 2 - 101137218 + 101099980 @@ -12135,32 +12175,32 @@ 1 2 - 6038694 + 5955966 2 3 - 1756736 + 1720612 3 4 - 853115 + 873268 4 6 - 966409 + 974240 6 12 - 854480 + 866446 12 7464 - 809436 + 807773 @@ -12176,17 +12216,17 @@ 1 2 - 9331039 + 9255286 2 3 - 1156142 + 1159810 3 17 - 791691 + 783213 @@ -12202,32 +12242,32 @@ 1 2 - 6227062 + 6146994 2 3 - 1726706 + 1685136 3 4 - 873590 + 893736 4 6 - 909080 + 918296 6 13 - 854480 + 866446 13 5239 - 687952 + 687699 @@ -12243,32 +12283,32 @@ 1 2 - 6433175 + 6350302 2 3 - 1613412 + 1577341 3 4 - 884510 + 904652 4 6 - 943204 + 951044 6 - 14 - 889970 + 13 + 854166 - 14 + 13 6287 - 514599 + 560802 @@ -12284,57 +12324,57 @@ 1 2 - 2729 + 2728 53 56 - 2729 + 2728 110 112 - 2729 + 2728 165 172 - 2729 + 2728 224 242 - 2729 + 2728 304 329 - 2729 + 2728 523 665 - 2729 + 2728 879 1140 - 2729 + 2728 1514 2087 - 2729 + 2728 3295 5932 - 2729 + 2728 15024 41276 - 2729 + 2728 @@ -12350,57 +12390,57 @@ 1 2 - 2729 + 2728 2 5 - 2729 + 2728 6 7 - 2729 + 2728 10 12 - 2729 + 2728 13 19 - 2729 + 2728 26 38 - 2729 + 2728 57 76 - 2729 + 2728 99 159 - 2729 + 2728 212 - 305 - 2729 + 306 + 2728 - 451 + 452 888 - 2729 + 2728 - 2378 - 6326 - 2729 + 2370 + 6266 + 2728 @@ -12416,57 +12456,57 @@ 1 2 - 2729 + 2728 53 56 - 2729 + 2728 110 112 - 2729 + 2728 165 172 - 2729 + 2728 224 242 - 2729 + 2728 304 329 - 2729 + 2728 523 665 - 2729 + 2728 879 1140 - 2729 + 2728 1514 2087 - 2729 + 2728 3295 5932 - 2729 + 2728 15024 41276 - 2729 + 2728 @@ -12482,57 +12522,57 @@ 1 2 - 2729 + 2728 2 5 - 2729 + 2728 6 8 - 2729 + 2728 10 13 - 2729 + 2728 14 28 - 2729 + 2728 37 62 - 2729 + 2728 97 137 - 2729 + 2728 192 342 - 2729 + 2728 553 963 - 2729 + 2728 1950 4155 - 2729 + 2728 10027 26335 - 2729 + 2728 @@ -12548,27 +12588,27 @@ 1 2 - 35832228 + 35819035 2 3 - 12411811 + 12407241 3 4 - 3598101 + 3596776 4 15 - 4264213 + 4262643 15 23 - 233412 + 233326 @@ -12584,17 +12624,17 @@ 1 2 - 39830270 + 39815605 2 3 - 12671158 + 12666492 3 23 - 3838338 + 3836925 @@ -12610,27 +12650,27 @@ 1 2 - 35832228 + 35819035 2 3 - 12411811 + 12407241 3 4 - 3598101 + 3596776 4 15 - 4264213 + 4262643 15 23 - 233412 + 233326 @@ -12646,27 +12686,27 @@ 1 2 - 35832228 + 35819035 2 3 - 12411811 + 12407241 3 4 - 3598101 + 3596776 4 15 - 4264213 + 4262643 15 23 - 233412 + 233326 @@ -12682,12 +12722,12 @@ 1 2 - 56895315 + 56874367 2 349 - 4430742 + 4429110 @@ -12703,12 +12743,12 @@ 1 2 - 59783624 + 59761613 2 349 - 1542433 + 1541865 @@ -12724,7 +12764,7 @@ 1 2 - 61326058 + 61303478 @@ -12740,12 +12780,12 @@ 1 2 - 56895315 + 56874367 2 349 - 4430742 + 4429110 @@ -12755,11 +12795,11 @@ paramsKotlinType - 101137218 + 101099980 id - 101137218 + 101099980 kttypeid @@ -12777,7 +12817,7 @@ 1 2 - 101137218 + 101099980 @@ -12803,15 +12843,15 @@ paramName - 101137218 + 10199508 id - 101137218 + 10199508 nodeName - 1688486 + 1666033 @@ -12825,7 +12865,7 @@ 1 2 - 101137218 + 10199508 @@ -12841,37 +12881,37 @@ 1 2 - 719347 + 729998 2 3 - 331691 + 338391 3 4 - 173353 + 169195 4 5 - 114658 + 117345 5 9 - 144688 + 140541 9 25 - 126943 + 126896 25 - 32680 - 77804 + 516 + 43663 @@ -12881,11 +12921,11 @@ isVarargsParam - 1005994 + 1005623 param - 1005994 + 1005623 @@ -13108,11 +13148,11 @@ isAnnotType - 29833 + 30064 interfaceid - 29833 + 30064 @@ -13396,11 +13436,11 @@ isEnumType - 350801 + 350672 classid - 350801 + 350672 @@ -13418,19 +13458,19 @@ typeVars - 5118694 + 5116810 id - 5118694 + 5116810 nodeName - 70979 + 70953 pos - 5459 + 5457 kind @@ -13438,7 +13478,7 @@ parentid - 3725044 + 3723673 @@ -13452,7 +13492,7 @@ 1 2 - 5118694 + 5116810 @@ -13468,7 +13508,7 @@ 1 2 - 5118694 + 5116810 @@ -13484,7 +13524,7 @@ 1 2 - 5118694 + 5116810 @@ -13500,7 +13540,7 @@ 1 2 - 5118694 + 5116810 @@ -13516,47 +13556,47 @@ 1 2 - 20474 + 20467 2 3 - 10919 + 10915 3 4 - 6824 + 6822 4 7 - 5459 + 5457 7 10 - 5459 + 5457 10 28 - 5459 + 5457 37 71 - 5459 + 5457 71 253 - 5459 + 5457 459 951 - 5459 + 5457 @@ -13572,17 +13612,17 @@ 1 2 - 45044 + 45027 2 3 - 16379 + 16373 3 4 - 8189 + 8186 4 @@ -13603,7 +13643,7 @@ 1 2 - 70979 + 70953 @@ -13619,47 +13659,47 @@ 1 2 - 20474 + 20467 2 3 - 10919 + 10915 3 4 - 6824 + 6822 4 7 - 5459 + 5457 7 10 - 5459 + 5457 10 28 - 5459 + 5457 37 71 - 5459 + 5457 71 253 - 5459 + 5457 459 951 - 5459 + 5457 @@ -13737,7 +13777,7 @@ 1 2 - 5459 + 5457 @@ -13848,17 +13888,17 @@ 1 2 - 2467893 + 2466984 2 3 - 1128842 + 1128427 3 5 - 128308 + 128261 @@ -13874,17 +13914,17 @@ 1 2 - 2467893 + 2466984 2 3 - 1128842 + 1128427 3 5 - 128308 + 128261 @@ -13900,17 +13940,17 @@ 1 2 - 2467893 + 2466984 2 3 - 1128842 + 1128427 3 5 - 128308 + 128261 @@ -13926,7 +13966,7 @@ 1 2 - 3725044 + 3723673 @@ -13936,19 +13976,19 @@ wildcards - 3116261 + 3637710 id - 3116261 + 3637710 nodeName - 859940 + 982427 kind - 2729 + 2728 @@ -13962,7 +14002,7 @@ 1 2 - 3116261 + 3637710 @@ -13978,7 +14018,7 @@ 1 2 - 3116261 + 3637710 @@ -13994,17 +14034,17 @@ 1 2 - 683857 + 791399 2 3 - 110563 + 120074 3 - 170 - 65519 + 214 + 70953 @@ -14020,7 +14060,7 @@ 1 2 - 859940 + 982427 @@ -14034,13 +14074,13 @@ 12 - 898 - 899 + 1027 + 1028 1364 - 1385 - 1386 + 1639 + 1640 1364 @@ -14055,13 +14095,13 @@ 12 - 196 - 197 + 222 + 223 1364 - 434 - 435 + 498 + 499 1364 @@ -14072,15 +14112,15 @@ typeBounds - 3872463 + 4393634 id - 3872463 + 4393634 typeid - 2723145 + 3191525 pos @@ -14088,7 +14128,7 @@ parentid - 3872463 + 4393634 @@ -14102,7 +14142,7 @@ 1 2 - 3872463 + 4393634 @@ -14118,7 +14158,7 @@ 1 2 - 3872463 + 4393634 @@ -14134,7 +14174,7 @@ 1 2 - 3872463 + 4393634 @@ -14150,17 +14190,17 @@ 1 2 - 2093887 + 2512012 2 3 - 552819 + 603101 3 - 51 - 76439 + 52 + 76411 @@ -14176,7 +14216,7 @@ 1 2 - 2723145 + 3191525 @@ -14192,17 +14232,17 @@ 1 2 - 2093887 + 2512012 2 3 - 552819 + 603101 3 - 51 - 76439 + 52 + 76411 @@ -14216,8 +14256,8 @@ 12 - 2837 - 2838 + 3220 + 3221 1364 @@ -14232,8 +14272,8 @@ 12 - 1995 - 1996 + 2339 + 2340 1364 @@ -14248,8 +14288,8 @@ 12 - 2837 - 2838 + 3220 + 3221 1364 @@ -14266,7 +14306,7 @@ 1 2 - 3872463 + 4393634 @@ -14282,7 +14322,7 @@ 1 2 - 3872463 + 4393634 @@ -14298,7 +14338,7 @@ 1 2 - 3872463 + 4393634 @@ -14604,37 +14644,37 @@ isParameterized - 24792227 + 25167883 memberid - 24792227 + 25167883 isRaw - 642908 + 642671 memberid - 642908 + 642671 erasure - 25435135 + 25810554 memberid - 25435135 + 25810554 erasureid - 868130 + 867810 @@ -14648,7 +14688,7 @@ 1 2 - 25435135 + 25810554 @@ -14664,57 +14704,57 @@ 1 2 - 144688 + 144635 2 3 - 133768 + 133719 3 4 - 88724 + 88691 4 5 - 55964 + 54579 5 6 - 51869 + 50485 6 8 - 62789 + 64130 8 11 - 77804 + 79139 11 19 - 70979 + 70953 19 33 - 70979 + 70953 33 93 - 65519 + 65495 97 - 1723 - 45044 + 1848 + 45027 @@ -14724,15 +14764,15 @@ isAnonymClass - 194713 + 195608 classid - 194713 + 195608 parent - 194713 + 195608 @@ -14746,7 +14786,7 @@ 1 2 - 194713 + 195608 @@ -14762,7 +14802,7 @@ 1 2 - 194713 + 195608 @@ -14772,15 +14812,15 @@ isLocalClassOrInterface - 4069 + 4110 typeid - 4069 + 4110 parent - 4069 + 4110 @@ -14794,7 +14834,7 @@ 1 2 - 4069 + 4110 @@ -14810,7 +14850,7 @@ 1 2 - 4069 + 4110 @@ -14831,15 +14871,15 @@ lambdaKind - 185816 + 186985 exprId - 185816 + 186985 bodyKind - 15 + 16 @@ -14853,7 +14893,7 @@ 1 2 - 185816 + 186985 @@ -14867,9 +14907,9 @@ 12 - 11987 - 11988 - 15 + 11644 + 11645 + 16 @@ -14879,27 +14919,27 @@ arrays - 1119287 + 1118875 id - 1119287 + 1118875 nodeName - 689317 + 689063 elementtypeid - 1112462 + 1112053 dimension - 2729 + 2728 componenttypeid - 1119287 + 1118875 @@ -14913,7 +14953,7 @@ 1 2 - 1119287 + 1118875 @@ -14929,7 +14969,7 @@ 1 2 - 1119287 + 1118875 @@ -14945,7 +14985,7 @@ 1 2 - 1119287 + 1118875 @@ -14961,7 +15001,7 @@ 1 2 - 1119287 + 1118875 @@ -14977,17 +15017,17 @@ 1 2 - 563738 + 563531 2 3 - 99643 + 99607 3 95 - 25934 + 25925 @@ -15003,17 +15043,17 @@ 1 2 - 563738 + 563531 2 3 - 99643 + 99607 3 95 - 25934 + 25925 @@ -15029,7 +15069,7 @@ 1 2 - 689317 + 689063 @@ -15045,17 +15085,17 @@ 1 2 - 563738 + 563531 2 3 - 99643 + 99607 3 95 - 25934 + 25925 @@ -15071,12 +15111,12 @@ 1 2 - 1105638 + 1105230 2 3 - 6824 + 6822 @@ -15092,12 +15132,12 @@ 1 2 - 1105638 + 1105230 2 3 - 6824 + 6822 @@ -15113,12 +15153,12 @@ 1 2 - 1105638 + 1105230 2 3 - 6824 + 6822 @@ -15134,12 +15174,12 @@ 1 2 - 1105638 + 1105230 2 3 - 6824 + 6822 @@ -15239,7 +15279,7 @@ 1 2 - 1119287 + 1118875 @@ -15255,7 +15295,7 @@ 1 2 - 1119287 + 1118875 @@ -15271,7 +15311,7 @@ 1 2 - 1119287 + 1118875 @@ -15287,7 +15327,7 @@ 1 2 - 1119287 + 1118875 @@ -15297,15 +15337,15 @@ enclInReftype - 3417923 + 3419393 child - 3417923 + 3419393 parent - 928189 + 927848 @@ -15319,7 +15359,7 @@ 1 2 - 3417923 + 3419393 @@ -15335,32 +15375,32 @@ 1 2 - 545994 + 545793 2 3 - 150148 + 150093 3 4 - 70979 + 70953 4 7 - 83264 + 83233 7 48 - 69614 + 69588 50 - 277 - 8189 + 279 + 8186 @@ -15370,15 +15410,15 @@ extendsReftype - 47612051 + 47979305 id1 - 33730150 + 34102515 id2 - 13910564 + 14050078 @@ -15392,22 +15432,22 @@ 1 2 - 25830981 + 26206254 2 3 - 3150385 + 3149226 3 4 - 3643145 + 3641804 4 10 - 1105638 + 1105230 @@ -15423,17 +15463,17 @@ 1 2 - 11822137 + 11956961 2 3 - 1423679 + 1429977 3 - 12114 - 664747 + 12283 + 663138 @@ -15526,15 +15566,15 @@ permits - 125 + 143 id1 - 32 + 36 id2 - 125 + 143 @@ -15548,12 +15588,12 @@ 1 2 - 2 + 1 2 3 - 9 + 12 3 @@ -15563,27 +15603,27 @@ 4 5 - 1 + 2 5 6 - 4 + 2 6 + 7 + 4 + + + 7 8 2 8 - 9 - 2 - - - 10 11 - 1 + 2 @@ -15599,7 +15639,7 @@ 1 2 - 125 + 143 @@ -15609,15 +15649,15 @@ hasModifier - 248786309 + 249505212 id1 - 166367134 + 166690663 id2 - 12284 + 13644 @@ -15631,17 +15671,17 @@ 1 2 - 84399769 + 84359142 2 3 - 81515555 + 81848494 3 4 - 451810 + 483026 @@ -15654,6 +15694,11 @@ 12 + + 31 + 32 + 1364 + 34 35 @@ -15680,13 +15725,13 @@ 1364 - 17754 - 17755 + 18033 + 18034 1364 - 18275 - 18276 + 18277 + 18278 1364 @@ -15695,8 +15740,8 @@ 1364 - 116552 - 116553 + 116834 + 116835 1364 @@ -16038,27 +16083,27 @@ stmts - 2534777 + 2533844 id - 2534777 + 2533844 kind - 13649 + 13644 parent - 1785400 + 1784743 idx - 217032 + 216952 bodydecl - 704332 + 704073 @@ -16072,7 +16117,7 @@ 1 2 - 2534777 + 2533844 @@ -16088,7 +16133,7 @@ 1 2 - 2534777 + 2533844 @@ -16104,7 +16149,7 @@ 1 2 - 2534777 + 2533844 @@ -16120,7 +16165,7 @@ 1 2 - 2534777 + 2533844 @@ -16136,7 +16181,7 @@ 2 3 - 4094 + 4093 4 @@ -16192,7 +16237,7 @@ 2 3 - 2729 + 2728 4 @@ -16243,7 +16288,7 @@ 1 2 - 5459 + 5457 2 @@ -16263,7 +16308,7 @@ 7 8 - 2729 + 2728 158 @@ -16289,7 +16334,7 @@ 2 3 - 4094 + 4093 25 @@ -16335,17 +16380,17 @@ 1 2 - 1502848 + 1502295 2 3 - 199287 + 199214 3 159 - 83264 + 83233 @@ -16361,17 +16406,17 @@ 1 2 - 1588842 + 1588257 2 3 - 189732 + 189663 3 4 - 6824 + 6822 @@ -16387,17 +16432,17 @@ 1 2 - 1502848 + 1502295 2 3 - 199287 + 199214 3 159 - 83264 + 83233 @@ -16413,7 +16458,7 @@ 1 2 - 1785400 + 1784743 @@ -16429,22 +16474,22 @@ 1 2 - 161068 + 161008 2 3 - 34124 + 34112 3 24 - 16379 + 16373 34 1211 - 5459 + 5457 @@ -16460,12 +16505,12 @@ 1 2 - 204747 + 204672 2 9 - 12284 + 12280 @@ -16481,22 +16526,22 @@ 1 2 - 161068 + 161008 2 3 - 34124 + 34112 3 24 - 16379 + 16373 34 1211 - 5459 + 5457 @@ -16512,22 +16557,22 @@ 1 2 - 161068 + 161008 2 3 - 34124 + 34112 3 19 - 16379 + 16373 29 517 - 5459 + 5457 @@ -16543,22 +16588,22 @@ 2 3 - 544629 + 544428 3 4 - 58694 + 58672 4 7 - 53234 + 53214 7 162 - 47774 + 47756 @@ -16574,17 +16619,17 @@ 2 3 - 576023 + 575811 3 4 - 81899 + 81868 4 7 - 46409 + 46392 @@ -16600,17 +16645,17 @@ 2 3 - 630623 + 630391 3 8 - 57329 + 57308 9 47 - 16379 + 16373 @@ -16626,22 +16671,22 @@ 1 2 - 544629 + 544428 2 3 - 92818 + 92784 3 7 - 54599 + 54579 7 159 - 12284 + 12280 @@ -17522,7 +17567,7 @@
    kttypeid - 12370 + 12368 @@ -17552,7 +17597,7 @@ 1 2 - 9277 + 9276 2 @@ -17560,8 +17605,8 @@ 2061 - 7177 - 7178 + 7178 + 7179 1030 @@ -17841,22 +17886,22 @@ when_if - 83447 + 84712 id - 83447 + 84712 when_branch_else - 80753 + 79813 id - 80753 + 79813 @@ -17901,12 +17946,12 @@ 1 2 - 162246 + 162245 2 3 - 33026 + 33027 3 @@ -17999,15 +18044,15 @@ propertyRefGetBinding - 9718 + 9660 id - 9718 + 9660 getter - 5873 + 5838 @@ -18021,7 +18066,7 @@ 1 2 - 9718 + 9660 @@ -18037,12 +18082,12 @@ 1 2 - 2132 + 2119 2 3 - 3646 + 3624 3 @@ -18105,15 +18150,15 @@ propertyRefSetBinding - 2651 + 2672 id - 2651 + 2672 setter - 1325 + 1336 @@ -18127,7 +18172,7 @@ 1 2 - 2651 + 2672 @@ -18143,7 +18188,7 @@ 2 3 - 1325 + 1336 @@ -18602,11 +18647,11 @@ localvarsKotlinType - 227623 + 227573 id - 227623 + 227573 kttypeid @@ -18624,7 +18669,7 @@ 1 2 - 227623 + 227573 @@ -20540,11 +20585,11 @@ xmlEncoding - 802611 + 802315 id - 802611 + 802315 encoding @@ -20562,7 +20607,7 @@ 1 2 - 802611 + 802315 @@ -20936,27 +20981,27 @@ xmlElements - 106792352 + 106753032 id - 106792352 + 106753032 name - 338516 + 338391 parentid - 2754540 + 2753526 idx - 1210741 + 1210296 fileid - 802611 + 802315 @@ -20970,7 +21015,7 @@ 1 2 - 106792352 + 106753032 @@ -20986,7 +21031,7 @@ 1 2 - 106792352 + 106753032 @@ -21002,7 +21047,7 @@ 1 2 - 106792352 + 106753032 @@ -21018,7 +21063,7 @@ 1 2 - 106792352 + 106753032 @@ -21034,57 +21079,57 @@ 1 2 - 106468 + 106429 2 3 - 40949 + 40934 3 4 - 16379 + 16373 4 6 - 30029 + 30018 6 8 - 24569 + 24560 8 9 - 12284 + 12280 9 10 - 23204 + 23196 10 18 - 28664 + 28654 18 48 - 25934 + 25925 52 250 - 25934 + 25925 342 73380 - 4094 + 4093 @@ -21100,52 +21145,52 @@ 1 2 - 124213 + 124167 2 3 - 46409 + 46392 3 4 - 17744 + 17738 4 5 - 15014 + 15009 5 6 - 19109 + 19102 6 8 - 28664 + 28654 8 10 - 28664 + 28654 10 21 - 27299 + 27289 22 128 - 25934 + 25925 130 229 - 5459 + 5457 @@ -21161,37 +21206,37 @@ 1 2 - 187002 + 186934 2 3 - 49139 + 49121 3 4 - 24569 + 24560 4 6 - 20474 + 20467 6 9 - 20474 + 20467 9 38 - 25934 + 25925 45 888 - 10919 + 10915 @@ -21207,42 +21252,42 @@ 1 2 - 184273 + 184205 2 3 - 36854 + 36841 3 4 - 17744 + 17738 4 5 - 13649 + 13644 5 7 - 30029 + 30018 7 16 - 25934 + 25925 17 114 - 27299 + 27289 118 131 - 2729 + 2728 @@ -21258,32 +21303,32 @@ 1 2 - 1676201 + 1675584 2 3 - 429970 + 429812 3 4 - 178813 + 178747 4 8 - 214302 + 214223 8 777 - 225222 + 225139 777 888 - 30029 + 30018 @@ -21299,17 +21344,17 @@ 1 2 - 2274065 + 2273228 2 3 - 292106 + 291999 3 17 - 188367 + 188298 @@ -21325,32 +21370,32 @@ 1 2 - 1676201 + 1675584 2 3 - 429970 + 429812 3 4 - 178813 + 178747 4 8 - 214302 + 214223 8 777 - 225222 + 225139 777 888 - 30029 + 30018 @@ -21366,7 +21411,7 @@ 1 2 - 2754540 + 2753526 @@ -21382,67 +21427,67 @@ 2 8 - 102373 + 102336 9 76 - 96913 + 96878 76 82 - 91454 + 91420 82 89 - 87359 + 87326 89 92 - 79169 + 79139 92 95 - 95548 + 95513 95 97 - 106468 + 106429 97 98 - 150148 + 150093 98 99 - 92818 + 92784 99 104 - 110563 + 110523 104 106 - 92818 + 92784 106 159 - 91454 + 91420 162 2019 - 13649 + 13644 @@ -21458,22 +21503,22 @@ 1 2 - 981424 + 981063 2 5 - 90089 + 90055 5 9 - 103738 + 103700 9 150 - 35489 + 35476 @@ -21489,67 +21534,67 @@ 2 8 - 102373 + 102336 9 76 - 96913 + 96878 76 82 - 91454 + 91420 82 89 - 87359 + 87326 89 92 - 79169 + 79139 92 95 - 95548 + 95513 95 97 - 106468 + 106429 97 98 - 150148 + 150093 98 99 - 92818 + 92784 99 104 - 110563 + 110523 104 106 - 92818 + 92784 106 159 - 91454 + 91420 162 2019 - 13649 + 13644 @@ -21565,67 +21610,67 @@ 2 8 - 102373 + 102336 9 76 - 96913 + 96878 76 82 - 91454 + 91420 82 89 - 87359 + 87326 89 92 - 79169 + 79139 92 95 - 95548 + 95513 95 97 - 106468 + 106429 97 98 - 150148 + 150093 98 99 - 92818 + 92784 99 104 - 110563 + 110523 104 106 - 92818 + 92784 106 139 - 91454 + 91420 141 589 - 13649 + 13644 @@ -21641,57 +21686,57 @@ 1 2 - 58694 + 58672 2 3 - 135133 + 135083 3 4 - 177448 + 177382 4 5 - 75074 + 75046 5 7 - 60059 + 60037 7 10 - 66884 + 66859 10 31 - 62789 + 62766 35 694 - 61424 + 61401 738 776 - 20474 + 20467 777 779 - 65519 + 65495 788 889 - 19109 + 19102 @@ -21707,37 +21752,37 @@ 1 2 - 58694 + 58672 2 3 - 399940 + 399793 3 4 - 139228 + 139177 4 5 - 65519 + 65495 5 6 - 61424 + 61401 6 9 - 65519 + 65495 9 69 - 12284 + 12280 @@ -21753,27 +21798,27 @@ 1 2 - 58694 + 58672 2 3 - 569198 + 568989 3 4 - 57329 + 57308 4 6 - 58694 + 58672 6 165 - 58694 + 58672 @@ -21789,42 +21834,42 @@ 1 2 - 203382 + 203307 2 3 - 219762 + 219681 3 4 - 88724 + 88691 4 7 - 66884 + 66859 7 17 - 66884 + 66859 18 763 - 61424 + 61401 764 777 - 65519 + 65495 777 888 - 30029 + 30018 @@ -21834,31 +21879,31 @@ xmlAttrs - 129898822 + 129850995 id - 129898822 + 129850995 elementid - 105883272 + 105844287 name - 513234 + 513045 value - 8206291 + 8203269 idx - 31394 + 31383 fileid - 801246 + 800951 @@ -21872,7 +21917,7 @@ 1 2 - 129898822 + 129850995 @@ -21888,7 +21933,7 @@ 1 2 - 129898822 + 129850995 @@ -21904,7 +21949,7 @@ 1 2 - 129898822 + 129850995 @@ -21920,7 +21965,7 @@ 1 2 - 129898822 + 129850995 @@ -21936,7 +21981,7 @@ 1 2 - 129898822 + 129850995 @@ -21952,17 +21997,17 @@ 1 2 - 96893479 + 96857804 2 6 - 7961959 + 7959027 6 24 - 1027833 + 1027455 @@ -21978,17 +22023,17 @@ 1 2 - 96893479 + 96857804 2 6 - 7975608 + 7972672 6 23 - 1014184 + 1013810 @@ -22004,17 +22049,17 @@ 1 2 - 96946713 + 96911018 2 6 - 8015193 + 8012242 6 21 - 921365 + 921025 @@ -22030,17 +22075,17 @@ 1 2 - 96893479 + 96857804 2 6 - 7961959 + 7959027 6 24 - 1027833 + 1027455 @@ -22056,7 +22101,7 @@ 1 2 - 105883272 + 105844287 @@ -22072,62 +22117,62 @@ 1 2 - 106468 + 106429 2 3 - 55964 + 55943 3 4 - 31394 + 31383 4 5 - 17744 + 17738 5 6 - 30029 + 30018 6 8 - 36854 + 36841 8 11 - 42314 + 42298 11 22 - 39584 + 39569 23 38 - 40949 + 40934 38 79 - 40949 + 40934 81 168 - 39584 + 39569 168 74700 - 31394 + 31383 @@ -22143,62 +22188,62 @@ 1 2 - 106468 + 106429 2 3 - 55964 + 55943 3 4 - 31394 + 31383 4 5 - 17744 + 17738 5 6 - 30029 + 30018 6 8 - 36854 + 36841 8 11 - 46409 + 46392 11 25 - 43679 + 43663 25 39 - 42314 + 42298 43 91 - 40949 + 40934 91 227 - 39584 + 39569 227 74700 - 21839 + 21831 @@ -22214,42 +22259,42 @@ 1 2 - 215667 + 215588 2 3 - 80534 + 80504 3 4 - 34124 + 34112 4 5 - 36854 + 36841 5 9 - 42314 + 42298 9 21 - 39584 + 39569 22 64 - 42314 + 42298 68 2100 - 21839 + 21831 @@ -22265,37 +22310,37 @@ 1 2 - 202017 + 201943 2 3 - 95548 + 95513 3 4 - 49139 + 49121 4 5 - 54599 + 54579 5 7 - 32759 + 32747 7 10 - 40949 + 40934 10 21 - 38219 + 38205 @@ -22311,52 +22356,52 @@ 1 2 - 178813 + 178747 2 3 - 53234 + 53214 3 4 - 27299 + 27289 4 5 - 24569 + 24560 5 6 - 38219 + 38205 6 9 - 42314 + 42298 9 17 - 45044 + 45027 17 34 - 39584 + 39569 36 91 - 39584 + 39569 91 223 - 24569 + 24560 @@ -22372,32 +22417,32 @@ 1 2 - 4482611 + 4480961 2 3 - 1213471 + 1213025 3 5 - 629258 + 629026 5 31 - 618338 + 618110 31 91 - 645638 + 645400 91 1111 - 615608 + 615381 3397 @@ -22418,32 +22463,32 @@ 1 2 - 4572700 + 4571017 2 3 - 1156142 + 1155716 3 5 - 637448 + 637213 5 33 - 647003 + 646764 33 93 - 633353 + 633119 93 3398 - 559643 + 559437 @@ -22459,17 +22504,17 @@ 1 2 - 7447359 + 7444617 2 4 - 660652 + 660409 4 53 - 98278 + 98242 @@ -22485,17 +22530,17 @@ 1 2 - 6796261 + 6793759 2 3 - 992344 + 991978 3 20 - 417685 + 417531 @@ -22511,32 +22556,32 @@ 1 2 - 5282492 + 5280548 2 3 - 889970 + 889642 3 10 - 637448 + 637213 10 83 - 623798 + 623568 83 99 - 626528 + 626297 99 182 - 146053 + 145999 @@ -22552,57 +22597,57 @@ 1 6 - 2729 + 2728 12 14 - 2729 + 2728 17 26 - 2729 + 2728 39 56 - 2729 + 2728 83 110 - 2729 + 2728 153 232 - 2729 + 2728 316 400 - 2729 + 2728 468 545 - 2729 + 2728 626 754 - 2729 + 2728 951 1491 - 2729 + 2728 4718 6587 - 2729 + 2728 77571 @@ -22623,57 +22668,57 @@ 1 6 - 2729 + 2728 12 14 - 2729 + 2728 17 26 - 2729 + 2728 39 56 - 2729 + 2728 83 110 - 2729 + 2728 153 232 - 2729 + 2728 316 400 - 2729 + 2728 468 545 - 2729 + 2728 626 754 - 2729 + 2728 951 1491 - 2729 + 2728 4718 6587 - 2729 + 2728 77571 @@ -22694,57 +22739,57 @@ 1 4 - 2729 + 2728 7 10 - 2729 + 2728 11 17 - 2729 + 2728 18 23 - 2729 + 2728 26 38 - 2729 + 2728 39 49 - 2729 + 2728 57 67 - 2729 + 2728 72 79 - 2729 + 2728 95 101 - 2729 + 2728 105 106 - 2729 + 2728 106 132 - 2729 + 2728 140 @@ -22765,57 +22810,57 @@ 1 5 - 2729 + 2728 7 10 - 2729 + 2728 11 18 - 2729 + 2728 22 32 - 2729 + 2728 46 63 - 2729 + 2728 85 119 - 2729 + 2728 142 185 - 2729 + 2728 212 228 - 2729 + 2728 253 275 - 2729 + 2728 307 423 - 2729 + 2728 580 1324 - 2729 + 2728 3579 @@ -22836,57 +22881,57 @@ 1 6 - 2729 + 2728 7 8 - 2729 + 2728 10 19 - 2729 + 2728 23 36 - 2729 + 2728 45 59 - 2729 + 2728 73 97 - 2729 + 2728 115 131 - 2729 + 2728 140 148 - 2729 + 2728 168 181 - 2729 + 2728 248 363 - 2729 + 2728 473 530 - 2729 + 2728 587 @@ -22907,72 +22952,72 @@ 1 3 - 60059 + 60037 3 5 - 61424 + 61401 5 6 - 36854 + 36841 6 7 - 61424 + 61401 7 8 - 51869 + 51850 8 10 - 58694 + 58672 10 15 - 65519 + 65495 15 27 - 61424 + 61401 27 41 - 61424 + 61401 41 65 - 61424 + 61401 65 157 - 65519 + 65495 162 817 - 61424 + 61401 818 832 - 66884 + 66859 832 1187 - 27299 + 27289 @@ -22988,52 +23033,52 @@ 1 2 - 91454 + 91420 2 3 - 188367 + 188298 3 4 - 113293 + 113252 4 5 - 77804 + 77775 5 8 - 72344 + 72317 8 14 - 61424 + 61401 14 295 - 61424 + 61401 330 775 - 50504 + 50485 776 778 - 65519 + 65495 787 888 - 19109 + 19102 @@ -23049,62 +23094,62 @@ 1 2 - 50504 + 50485 2 3 - 65519 + 65495 3 4 - 50504 + 50485 4 5 - 66884 + 66859 5 6 - 121483 + 121438 6 7 - 114658 + 114616 7 8 - 50504 + 50485 8 12 - 61424 + 61401 12 18 - 69614 + 69588 18 24 - 68249 + 68224 24 37 - 62789 + 62766 37 55 - 19109 + 19102 @@ -23120,67 +23165,67 @@ 1 3 - 69614 + 69588 3 4 - 39584 + 39569 4 5 - 73709 + 73682 5 6 - 88724 + 88691 6 8 - 62789 + 62766 8 12 - 70979 + 70953 12 19 - 61424 + 61401 19 27 - 69614 + 69588 27 41 - 61424 + 61401 42 170 - 61424 + 61401 205 780 - 57329 + 57308 781 783 - 65519 + 65495 791 893 - 19109 + 19102 @@ -23196,47 +23241,47 @@ 1 2 - 79169 + 79139 2 3 - 76439 + 76411 3 4 - 151513 + 151457 4 5 - 155608 + 155551 5 6 - 92818 + 92784 6 10 - 68249 + 68224 10 12 - 46409 + 46392 12 15 - 69614 + 69588 15 24 - 61424 + 61401 @@ -23246,23 +23291,23 @@ xmlNs - 1287181 + 1286707 id - 8189 + 8186 prefixName - 9554 + 9551 URI - 8189 + 8186 fileid - 749376 + 749100 @@ -23276,7 +23321,7 @@ 1 2 - 6824 + 6822 2 @@ -23297,7 +23342,7 @@ 1 2 - 8189 + 8186 @@ -23354,7 +23399,7 @@ 1 2 - 9554 + 9551 @@ -23370,7 +23415,7 @@ 1 2 - 9554 + 9551 @@ -23432,7 +23477,7 @@ 1 2 - 8189 + 8186 @@ -23448,7 +23493,7 @@ 1 2 - 6824 + 6822 2 @@ -23510,17 +23555,17 @@ 1 2 - 334421 + 334298 2 3 - 292106 + 291999 3 4 - 122848 + 122803 @@ -23536,17 +23581,17 @@ 1 2 - 334421 + 334298 2 3 - 292106 + 291999 3 4 - 122848 + 122803 @@ -23562,17 +23607,17 @@ 1 2 - 334421 + 334298 2 3 - 292106 + 291999 3 4 - 122848 + 122803 @@ -23582,19 +23627,19 @@ xmlHasNs - 25966114 + 25956554 elementId - 25966114 + 25956554 nsId - 8189 + 8186 fileid - 745281 + 745007 @@ -23608,7 +23653,7 @@ 1 2 - 25966114 + 25956554 @@ -23624,7 +23669,7 @@ 1 2 - 25966114 + 25956554 @@ -23722,77 +23767,77 @@ 1 3 - 45044 + 45027 3 5 - 62789 + 62766 5 6 - 34124 + 34112 6 7 - 65519 + 65495 7 8 - 49139 + 49121 8 10 - 61424 + 61401 10 15 - 65519 + 65495 15 25 - 55964 + 55943 25 36 - 57329 + 57308 36 49 - 58694 + 58672 49 54 - 15014 + 15009 54 55 - 58694 + 58672 55 81 - 57329 + 57308 81 298 - 55964 + 55943 298 833 - 2729 + 2728 @@ -23808,17 +23853,17 @@ 1 2 - 335786 + 335662 2 3 - 289376 + 289270 3 4 - 120118 + 120074 @@ -23828,23 +23873,23 @@ xmlComments - 107485764 + 107446189 id - 107485764 + 107446189 text - 1696676 + 1696051 parentid - 842195 + 841885 fileid - 787596 + 787306 @@ -23858,7 +23903,7 @@ 1 2 - 107485764 + 107446189 @@ -23874,7 +23919,7 @@ 1 2 - 107485764 + 107446189 @@ -23890,7 +23935,7 @@ 1 2 - 107485764 + 107446189 @@ -23906,67 +23951,67 @@ 1 2 - 233412 + 233326 2 7 - 139228 + 139177 7 32 - 140593 + 140541 32 61 - 128308 + 128261 61 76 - 128308 + 128261 76 84 - 136498 + 136448 84 90 - 125578 + 125532 90 94 - 111928 + 111887 94 95 - 60059 + 60037 95 96 - 101008 + 100971 96 98 - 140593 + 140541 98 100 - 126943 + 126896 100 460 - 124213 + 124167 @@ -23982,67 +24027,67 @@ 1 2 - 236142 + 236055 2 6 - 135133 + 135083 6 32 - 143323 + 143270 32 61 - 129673 + 129625 61 75 - 132403 + 132354 75 84 - 148783 + 148728 84 90 - 122848 + 122803 90 94 - 120118 + 120074 94 95 - 66884 + 66859 95 96 - 103738 + 103700 96 98 - 143323 + 143270 98 100 - 135133 + 135083 100 460 - 79169 + 79139 @@ -24058,67 +24103,67 @@ 1 2 - 247062 + 246971 2 7 - 133768 + 133719 7 32 - 133768 + 133719 32 61 - 129673 + 129625 61 75 - 132403 + 132354 75 84 - 148783 + 148728 84 90 - 122848 + 122803 90 94 - 120118 + 120074 94 95 - 66884 + 66859 95 96 - 103738 + 103700 96 98 - 143323 + 143270 98 100 - 135133 + 135083 100 460 - 79169 + 79139 @@ -24134,22 +24179,22 @@ 1 2 - 670207 + 669961 2 724 - 64154 + 64130 726 830 - 77804 + 77775 831 941 - 30029 + 30018 @@ -24165,27 +24210,27 @@ 1 2 - 670207 + 669961 2 697 - 64154 + 64130 697 795 - 34124 + 34112 795 827 - 64154 + 64130 838 899 - 9554 + 9551 @@ -24201,7 +24246,7 @@ 1 2 - 842195 + 841885 @@ -24217,27 +24262,27 @@ 1 2 - 601958 + 601736 2 549 - 60059 + 60037 579 829 - 40949 + 40934 829 832 - 65519 + 65495 834 941 - 19109 + 19102 @@ -24253,27 +24298,27 @@ 1 2 - 601958 + 601736 2 536 - 60059 + 60037 560 795 - 51869 + 51850 795 812 - 60059 + 60037 819 899 - 13649 + 13644 @@ -24289,12 +24334,12 @@ 1 2 - 748011 + 747736 2 6 - 39584 + 39569 @@ -24304,19 +24349,19 @@ xmlChars - 101574013 + 101536615 id - 101574013 + 101536615 text - 78006178 + 77977457 parentid - 101574013 + 101536615 idx @@ -24324,11 +24369,11 @@ isCDATA - 2729 + 2728 fileid - 177448 + 177382 @@ -24342,7 +24387,7 @@ 1 2 - 101574013 + 101536615 @@ -24358,7 +24403,7 @@ 1 2 - 101574013 + 101536615 @@ -24374,7 +24419,7 @@ 1 2 - 101574013 + 101536615 @@ -24390,7 +24435,7 @@ 1 2 - 101574013 + 101536615 @@ -24406,7 +24451,7 @@ 1 2 - 101574013 + 101536615 @@ -24422,17 +24467,17 @@ 1 2 - 66746414 + 66721839 2 3 - 7010564 + 7007983 3 128 - 4249199 + 4247634 @@ -24448,17 +24493,17 @@ 1 2 - 66746414 + 66721839 2 3 - 7010564 + 7007983 3 128 - 4249199 + 4247634 @@ -24474,7 +24519,7 @@ 1 2 - 78006178 + 77977457 @@ -24490,7 +24535,7 @@ 1 2 - 78006178 + 77977457 @@ -24506,12 +24551,12 @@ 1 2 - 74636029 + 74608549 2 76 - 3370148 + 3368907 @@ -24527,7 +24572,7 @@ 1 2 - 101574013 + 101536615 @@ -24543,7 +24588,7 @@ 1 2 - 101574013 + 101536615 @@ -24559,7 +24604,7 @@ 1 2 - 101574013 + 101536615 @@ -24575,7 +24620,7 @@ 1 2 - 101574013 + 101536615 @@ -24591,7 +24636,7 @@ 1 2 - 101574013 + 101536615 @@ -24750,7 +24795,7 @@ 1 2 - 2729 + 2728 @@ -24787,57 +24832,57 @@ 1 2 - 15014 + 15009 2 23 - 13649 + 13644 24 243 - 13649 + 13644 294 566 - 13649 + 13644 610 686 - 13649 + 13644 691 764 - 13649 + 13644 765 775 - 13649 + 13644 775 776 - 4094 + 4093 776 777 - 49139 + 49121 777 803 - 13649 + 13644 807 888 - 13649 + 13644 @@ -24853,67 +24898,67 @@ 1 2 - 15014 + 15009 2 21 - 13649 + 13644 22 188 - 13649 + 13644 208 492 - 13649 + 13644 525 589 - 13649 + 13644 590 638 - 13649 + 13644 639 651 - 13649 + 13644 652 656 - 12284 + 12280 656 659 - 16379 + 16373 659 663 - 15014 + 15009 663 667 - 13649 + 13644 667 701 - 13649 + 13644 702 744 - 9554 + 9551 @@ -24929,57 +24974,57 @@ 1 2 - 15014 + 15009 2 23 - 13649 + 13644 24 243 - 13649 + 13644 294 566 - 13649 + 13644 610 686 - 13649 + 13644 691 764 - 13649 + 13644 765 775 - 13649 + 13644 775 776 - 4094 + 4093 776 777 - 49139 + 49121 777 803 - 13649 + 13644 807 888 - 13649 + 13644 @@ -24995,7 +25040,7 @@ 1 2 - 177448 + 177382 @@ -25011,12 +25056,12 @@ 1 2 - 43679 + 43663 2 3 - 133768 + 133719 @@ -25026,15 +25071,15 @@ xmllocations - 447840746 + 447675856 xmlElement - 446561755 + 446397336 location - 419653800 + 419499288 @@ -25048,12 +25093,12 @@ 1 2 - 446553565 + 446389149 2 454 - 8189 + 8186 @@ -25069,12 +25114,12 @@ 1 2 - 410141218 + 409990208 2 25 - 9512582 + 9509079 @@ -25315,19 +25360,19 @@ ktComments - 133768 + 133719 id - 133768 + 133719 kind - 4094 + 4093 text - 96913 + 96878 @@ -25341,7 +25386,7 @@ 1 2 - 133768 + 133719 @@ -25357,7 +25402,7 @@ 1 2 - 133768 + 133719 @@ -25425,12 +25470,12 @@ 1 2 - 92818 + 92784 4 23 - 4094 + 4093 @@ -25446,7 +25491,7 @@ 1 2 - 96913 + 96878 @@ -25456,19 +25501,19 @@ ktCommentSections - 59246 + 59191 id - 59246 + 59191 comment - 54224 + 54069 content - 50410 + 50263 @@ -25482,7 +25527,7 @@ 1 2 - 59246 + 59191 @@ -25498,7 +25543,7 @@ 1 2 - 59246 + 59191 @@ -25514,12 +25559,12 @@ 1 2 - 52208 + 52013 2 18 - 2015 + 2055 @@ -25535,12 +25580,12 @@ 1 2 - 52208 + 52013 2 18 - 2015 + 2055 @@ -25556,17 +25601,17 @@ 1 2 - 44628 + 44482 2 3 - 4836 + 4801 3 63 - 945 + 979 @@ -25582,17 +25627,17 @@ 1 2 - 44737 + 44594 2 3 - 4743 + 4705 3 56 - 930 + 963 @@ -25602,15 +25647,15 @@ ktCommentSectionNames - 5022 + 5122 id - 5022 + 5122 name - 15 + 16 @@ -25624,7 +25669,7 @@ 1 2 - 5022 + 5122 @@ -25638,9 +25683,9 @@ 12 - 324 - 325 - 15 + 319 + 320 + 16 @@ -25650,15 +25695,15 @@ ktCommentSectionSubjectNames - 5022 + 5122 id - 5022 + 5122 subjectname - 3301 + 3388 @@ -25672,7 +25717,7 @@ 1 2 - 5022 + 5122 @@ -25688,22 +25733,22 @@ 1 2 - 2511 + 2601 2 3 - 496 + 497 3 - 9 - 248 + 10 + 256 - 10 - 15 - 46 + 13 + 16 + 32 @@ -25713,15 +25758,15 @@ ktCommentOwners - 84203 + 82540 id - 53480 + 53314 owner - 82173 + 80437 @@ -25735,22 +25780,22 @@ 1 2 - 34335 + 34638 2 3 - 12184 + 12076 3 4 - 4526 + 4496 4 6 - 2433 + 2103 @@ -25766,12 +25811,12 @@ 1 2 - 80142 + 78333 2 3 - 2030 + 2103 @@ -25781,15 +25826,15 @@ ktExtensionFunctions - 702967 + 702708 id - 702967 + 702708 typeid - 84629 + 84597 kttypeid @@ -25807,7 +25852,7 @@ 1 2 - 702967 + 702708 @@ -25823,7 +25868,7 @@ 1 2 - 702967 + 702708 @@ -25839,12 +25884,12 @@ 1 2 - 53234 + 53214 2 3 - 6824 + 6822 3 @@ -25854,22 +25899,22 @@ 4 5 - 6824 + 6822 5 12 - 6824 + 6822 12 69 - 6824 + 6822 84 174 - 2729 + 2728 @@ -25885,7 +25930,7 @@ 1 2 - 84629 + 84597 @@ -25927,15 +25972,15 @@ ktProperties - 30317687 + 30306525 id - 30317687 + 30306525 nodeName - 10696024 + 10692086 @@ -25949,7 +25994,7 @@ 1 2 - 30317687 + 30306525 @@ -25965,22 +26010,22 @@ 1 2 - 7889614 + 7886709 2 3 - 1216201 + 1215754 3 8 - 809436 + 809138 8 554 - 780771 + 780484 @@ -25990,15 +26035,15 @@ ktPropertyGetters - 4569970 + 4568288 id - 4569970 + 4568288 getter - 4569970 + 4568288 @@ -26012,7 +26057,7 @@ 1 2 - 4569970 + 4568288 @@ -26028,7 +26073,7 @@ 1 2 - 4569970 + 4568288 @@ -26038,15 +26083,15 @@ ktPropertySetters - 290741 + 290634 id - 290741 + 290634 setter - 290741 + 290634 @@ -26060,7 +26105,7 @@ 1 2 - 290741 + 290634 @@ -26076,7 +26121,7 @@ 1 2 - 290741 + 290634 @@ -26086,15 +26131,15 @@ ktPropertyBackingFields - 23615610 + 23606915 id - 23615610 + 23606915 backingField - 23615610 + 23606915 @@ -26108,7 +26153,7 @@ 1 2 - 23615610 + 23606915 @@ -26124,7 +26169,7 @@ 1 2 - 23615610 + 23606915 @@ -26134,11 +26179,11 @@ ktSyntheticBody - 10308 + 10307 id - 10308 + 10307 kind @@ -26156,7 +26201,7 @@ 1 2 - 10308 + 10307 @@ -26182,37 +26227,37 @@ ktLocalFunction - 2729 + 2728 id - 2729 + 2728 ktInitializerAssignment - 393115 + 392971 id - 393115 + 392971 ktPropertyDelegates - 5698 + 5664 id - 5698 + 5664 variableId - 5698 + 5664 @@ -26226,7 +26271,7 @@ 1 2 - 5698 + 5664 @@ -26242,7 +26287,7 @@ 1 2 - 5698 + 5664 @@ -26252,15 +26297,15 @@ compiler_generated - 120 + 491650 id - 120 + 491650 kind - 2 + 5153 @@ -26274,7 +26319,7 @@ 1 2 - 120 + 491650 @@ -26288,9 +26333,29 @@ 12 - 42 - 43 - 2 + 2 + 3 + 1030 + + + 5 + 6 + 1030 + + + 10 + 11 + 1030 + + + 184 + 185 + 1030 + + + 276 + 277 + 1030 @@ -26300,15 +26365,15 @@ ktFunctionOriginalNames - 981424 + 1147529 id - 981424 + 1147529 name - 66884 + 79139 @@ -26322,7 +26387,7 @@ 1 2 - 981424 + 1147529 @@ -26338,22 +26403,27 @@ 1 2 - 53234 + 55943 2 - 3 - 5459 + 4 + 6822 - 3 - 96 - 5459 + 7 + 14 + 4093 - 96 + 16 + 31 + 6822 + + + 92 380 - 2729 + 5457 diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index 541fad197e0..5fe704a4f35 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-all -version: 0.3.1-dev +version: 0.3.3-dev groups: java dbscheme: config/semmlecode.dbscheme extractor: java diff --git a/java/ql/lib/semmle/code/Location.qll b/java/ql/lib/semmle/code/Location.qll index d977395a83c..8bb81becb11 100755 --- a/java/ql/lib/semmle/code/Location.qll +++ b/java/ql/lib/semmle/code/Location.qll @@ -47,6 +47,8 @@ predicate hasName(Element e, string name) { kt_type_alias(e, name, _) or ktProperties(e, name) + or + e instanceof ErrorType and name = "" } /** diff --git a/java/ql/lib/semmle/code/java/Expr.qll b/java/ql/lib/semmle/code/java/Expr.qll index 48023e135af..2c969a8d442 100755 --- a/java/ql/lib/semmle/code/java/Expr.qll +++ b/java/ql/lib/semmle/code/java/Expr.qll @@ -100,6 +100,18 @@ class Expr extends ExprParent, @expr { /** Holds if this expression is parenthesized. */ predicate isParenthesized() { isParenthesized(this, _) } + + /** + * Gets the underlying expression looking through casts and not-nulls, if any. + * Otherwise just gets this expression. + */ + Expr getUnderlyingExpr() { + if this instanceof CastingExpr or this instanceof NotNullExpr + then + result = this.(CastingExpr).getExpr().getUnderlyingExpr() or + result = this.(NotNullExpr).getExpr().getUnderlyingExpr() + else result = this + } } /** diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll index a37f9810c44..256df5d66f3 100755 --- a/java/ql/lib/semmle/code/java/Type.qll +++ b/java/ql/lib/semmle/code/java/Type.qll @@ -413,8 +413,12 @@ class RefType extends Type, Annotatable, Modifiable, @reftype { /** Gets a direct or indirect supertype of this type, including itself. */ RefType getAnAncestor() { hasDescendant(result, this) } - /** Gets a direct or indirect supertype of this type, not including itself. */ - RefType getAStrictAncestor() { result = this.getAnAncestor() and result != this } + /** + * Gets a direct or indirect supertype of this type. + * This does not including itself, unless this type is part of a cycle + * in the type hierarchy. + */ + RefType getAStrictAncestor() { result = this.getASupertype().getAnAncestor() } /** * Gets the source declaration of a direct supertype of this type, excluding itself. @@ -666,6 +670,14 @@ class RefType extends Type, Annotatable, Modifiable, @reftype { } } +/** + * An `ErrorType` is generated when CodeQL is unable to correctly + * extract a type. + */ +class ErrorType extends RefType, @errortype { + override string getAPrimaryQlClass() { result = "ErrorType" } +} + /** A type that is the same as its source declaration. */ class SrcRefType extends RefType { SrcRefType() { this.isSourceDeclaration() } @@ -1182,8 +1194,8 @@ private Type erase(Type t) { } /** - * Is there a common (reflexive, transitive) subtype of the erasures of - * types `t1` and `t2`? + * Holds if there is a common (reflexive, transitive) subtype of the erasures of + * types `t1` and `t2`. * * If there is no such common subtype, then the two types are disjoint. * However, the converse is not true; for example, the parameterized types @@ -1200,6 +1212,25 @@ predicate haveIntersection(RefType t1, RefType t2) { ) } +/** + * Holds if there is no common (reflexive, transitive) subtype of the erasures + * of types `t1` and `t2`. + * + * If there is no such common subtype, then the two types are disjoint. + * However, the converse is not true; for example, the parameterized types + * `List` and `Collection` are disjoint, + * but their erasures (`List` and `Collection`, respectively) + * do have common subtypes (such as `List` itself). + * + * For the definition of the notion of *erasure* see JLS v8, section 4.6 (Type Erasure). + */ +bindingset[t1, t2] +predicate notHaveIntersection(RefType t1, RefType t2) { + exists(RefType e1, RefType e2 | e1 = erase(t1) and e2 = erase(t2) | + not erasedHaveIntersection(e1, e2) + ) +} + /** * Holds if there is a common (reflexive, transitive) subtype of the erased * types `t1` and `t2`. diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll index 0f5d5e39f39..6c4a369527c 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/ContainerFlow.qll @@ -241,8 +241,29 @@ private class ContainerFlowSummaries extends SummaryModelCsv { "java.util;NavigableSet;true;pollLast;();;Argument[-1].Element;ReturnValue;value;manual", "java.util;NavigableSet;true;subSet;(Object,boolean,Object,boolean);;Argument[-1].Element;ReturnValue.Element;value;manual", "java.util;NavigableSet;true;tailSet;(Object,boolean);;Argument[-1].Element;ReturnValue.Element;value;manual", - "java.util;Scanner;true;next;(Pattern);;Argument[-1];ReturnValue;taint;manual", - "java.util;Scanner;true;next;(String);;Argument[-1];ReturnValue;taint;manual", + "java.util;Properties;true;getProperty;(String);;Argument[-1].MapValue;ReturnValue;value;manual", + "java.util;Properties;true;getProperty;(String,String);;Argument[-1].MapValue;ReturnValue;value;manual", + "java.util;Properties;true;getProperty;(String,String);;Argument[1];ReturnValue;value;manual", + "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual", + "java.util;Scanner;true;findInLine;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextBigDecimal;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextBigInteger;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextBoolean;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextByte;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextDouble;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextFloat;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextInt;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextLine;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextLong;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;nextShort;;;Argument[-1];ReturnValue;taint;manual", + "java.util;Scanner;true;reset;;;Argument[-1];ReturnValue;value;manual", + "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual", + "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual", + "java.util;Scanner;true;useLocale;;;Argument[-1];ReturnValue;value;manual", + "java.util;Scanner;true;useRadix;;;Argument[-1];ReturnValue;value;manual", "java.util;SortedMap;true;headMap;(Object);;Argument[-1].MapKey;ReturnValue.MapKey;value;manual", "java.util;SortedMap;true;headMap;(Object);;Argument[-1].MapValue;ReturnValue.MapValue;value;manual", "java.util;SortedMap;true;subMap;(Object,Object);;Argument[-1].MapKey;ReturnValue.MapKey;value;manual", diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index a076f7a2e45..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index a076f7a2e45..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index a076f7a2e45..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index a076f7a2e45..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index a076f7a2e45..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll index a076f7a2e45..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForOnActivityResult.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll index a076f7a2e45..340bfe280b7 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImplForSerializability.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll b/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll index 7dd250671ad..abab4134253 100644 --- a/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll +++ b/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll @@ -19,8 +19,9 @@ private predicate runner(Method m, int n, Method runmethod) { exists(Parameter p, MethodAccess ma, int j | p = m.getParameter(n) and ma.getEnclosingCallable() = m and - runner(ma.getMethod().getSourceDeclaration(), j, _) and - ma.getArgument(j) = p.getAnAccess() + runner(pragma[only_bind_into](ma.getMethod().getSourceDeclaration()), + pragma[only_bind_into](j), _) and + ma.getArgument(pragma[only_bind_into](j)) = p.getAnAccess() ) ) } diff --git a/java/ql/lib/semmle/code/java/frameworks/Assertions.qll b/java/ql/lib/semmle/code/java/frameworks/Assertions.qll index 3c3d090b993..ff95c71b037 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Assertions.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Assertions.qll @@ -2,7 +2,8 @@ * A library providing uniform access to various assertion frameworks. * * Currently supports `org.junit.Assert`, `junit.framework.*`, - * `com.google.common.base.Preconditions`, and `java.util.Objects`. + * `org.junit.jupiter.api.Assertions`, `com.google.common.base.Preconditions`, + * and `java.util.Objects`. */ import java @@ -17,7 +18,11 @@ private newtype AssertKind = private predicate assertionMethod(Method m, AssertKind kind) { exists(RefType junit | m.getDeclaringType() = junit and - (junit.hasQualifiedName("org.junit", "Assert") or junit.hasQualifiedName("junit.framework", _)) + ( + junit.hasQualifiedName("org.junit", "Assert") or + junit.hasQualifiedName("junit.framework", _) or + junit.hasQualifiedName("org.junit.jupiter.api", "Assertions") + ) | m.hasName("assertNotNull") and kind = AssertKindNotNull() or diff --git a/java/ql/lib/semmle/code/java/frameworks/Properties.qll b/java/ql/lib/semmle/code/java/frameworks/Properties.qll index 7b749a13e05..0c7b83b2e52 100644 --- a/java/ql/lib/semmle/code/java/frameworks/Properties.qll +++ b/java/ql/lib/semmle/code/java/frameworks/Properties.qll @@ -10,13 +10,11 @@ class TypeProperty extends Class { } /** The `getProperty` method of the class `java.util.Properties`. */ -class PropertiesGetPropertyMethod extends ValuePreservingMethod { +class PropertiesGetPropertyMethod extends Method { PropertiesGetPropertyMethod() { getDeclaringType() instanceof TypeProperty and hasName("getProperty") } - - override predicate returnsValue(int arg) { arg = 1 } } /** The `get` method of the class `java.util.Properties`. */ diff --git a/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll b/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll index a4e7d11c549..1e739bcba2b 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/AsyncTask.qll @@ -4,19 +4,62 @@ import java private import semmle.code.java.dataflow.DataFlow private import semmle.code.java.dataflow.FlowSteps -/** - * Models the value-preserving step from `asyncTask.execute(params)` to `AsyncTask::doInBackground(params)`. +/* + * The following flow steps aim to model the life-cycle of `AsyncTask`s described here: + * https://developer.android.com/reference/android/os/AsyncTask#the-4-steps */ -private class AsyncTaskAdditionalValueStep extends AdditionalValueStep { + +/** + * A taint step from the vararg arguments of `AsyncTask::execute` and `AsyncTask::executeOnExecutor` + * to the parameter of `AsyncTask::doInBackground`. + */ +private class AsyncTaskExecuteAdditionalValueStep extends AdditionalTaintStep { override predicate step(DataFlow::Node node1, DataFlow::Node node2) { exists(ExecuteAsyncTaskMethodAccess ma, AsyncTaskRunInBackgroundMethod m | - DataFlow::getInstanceArgument(ma).getType() = m.getDeclaringType() and + DataFlow::getInstanceArgument(ma).getType() = m.getDeclaringType() + | node1.asExpr() = ma.getParamsArgument() and node2.asParameter() = m.getParameter(0) ) } } +/** + * A value-preserving step from the return value of `AsyncTask::doInBackground` + * to the parameter of `AsyncTask::onPostExecute`. + */ +private class AsyncTaskOnPostExecuteAdditionalValueStep extends AdditionalValueStep { + override predicate step(DataFlow::Node node1, DataFlow::Node node2) { + exists( + AsyncTaskRunInBackgroundMethod runInBackground, AsyncTaskOnPostExecuteMethod onPostExecute + | + onPostExecute.getDeclaringType() = runInBackground.getDeclaringType() + | + node1.asExpr() = any(ReturnStmt r | r.getEnclosingCallable() = runInBackground).getResult() and + node2.asParameter() = onPostExecute.getParameter(0) + ) + } +} + +/** + * A value-preserving step from field initializers in `AsyncTask`'s constructor or initializer method + * to the instance parameter of `AsyncTask::runInBackground` and `AsyncTask::onPostExecute`. + */ +private class AsyncTaskFieldInitQualifierToInstanceParameterStep extends AdditionalValueStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(AsyncTaskInit init, Callable receiver | + n1.(DataFlow::PostUpdateNode).getPreUpdateNode() = + DataFlow::getFieldQualifier(any(FieldWrite f | f.getEnclosingCallable() = init)) and + n2.(DataFlow::InstanceParameterNode).getCallable() = receiver and + receiver.getDeclaringType() = init.getDeclaringType() and + ( + receiver instanceof AsyncTaskRunInBackgroundMethod or + receiver instanceof AsyncTaskOnPostExecuteMethod + ) + ) + } +} + /** * The Android class `android.os.AsyncTask`. */ @@ -24,29 +67,38 @@ private class AsyncTask extends RefType { AsyncTask() { this.hasQualifiedName("android.os", "AsyncTask") } } +/** The constructor or initializer method of the `android.os.AsyncTask` class. */ +private class AsyncTaskInit extends Callable { + AsyncTaskInit() { + this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and + (this instanceof Constructor or this instanceof InitializerMethod) + } +} + /** A call to the `execute` or `executeOnExecutor` methods of the `android.os.AsyncTask` class. */ private class ExecuteAsyncTaskMethodAccess extends MethodAccess { - Argument paramsArgument; - ExecuteAsyncTaskMethodAccess() { - exists(Method m | - this.getMethod() = m and - m.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask - | - m.getName() = "execute" and not m.isStatic() and paramsArgument = this.getArgument(0) - or - m.getName() = "executeOnExecutor" and paramsArgument = this.getArgument(1) - ) + this.getMethod().hasName(["execute", "executeOnExecutor"]) and + this.getMethod().getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof + AsyncTask } /** Returns the `params` argument of this call. */ - Argument getParamsArgument() { result = paramsArgument } + Argument getParamsArgument() { result = this.getAnArgument() and result.isVararg() } } /** The `doInBackground` method of the `android.os.AsyncTask` class. */ private class AsyncTaskRunInBackgroundMethod extends Method { AsyncTaskRunInBackgroundMethod() { this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and - this.getName() = "doInBackground" + this.hasName("doInBackground") + } +} + +/** The `onPostExecute` method of the `android.os.AsyncTask` class. */ +private class AsyncTaskOnPostExecuteMethod extends Method { + AsyncTaskOnPostExecuteMethod() { + this.getDeclaringType().getSourceDeclaration().getASourceSupertype*() instanceof AsyncTask and + this.hasName("onPostExecute") } } diff --git a/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll new file mode 100644 index 00000000000..3d47f77a2bb --- /dev/null +++ b/java/ql/lib/semmle/code/java/security/AndroidWebViewCertificateValidationQuery.qll @@ -0,0 +1,29 @@ +/** Definitions for the web view certificate validation query */ + +import java + +/** A method that overrides `WebViewClient.onReceivedSslError` */ +class OnReceivedSslErrorMethod extends Method { + OnReceivedSslErrorMethod() { + this.overrides*(any(Method m | + m.hasQualifiedName("android.webkit", "WebViewClient", "onReceivedSslError") + )) + } + + /** Gets the `SslErrorHandler` argument to this method. */ + Parameter handlerArg() { result = this.getParameter(1) } +} + +/** A call to `SslErrorHandler.proceed` */ +private class SslProceedCall extends MethodAccess { + SslProceedCall() { + this.getMethod().hasQualifiedName("android.webkit", "SslErrorHandler", "proceed") + } +} + +/** Holds if `m` trusts all certificates by calling `SslErrorHandler.proceed` unconditionally. */ +predicate trustsAllCerts(OnReceivedSslErrorMethod m) { + exists(SslProceedCall pr | pr.getQualifier().(VarAccess).getVariable() = m.handlerArg() | + pr.getBasicBlock().bbPostDominates(m.getBody().getBasicBlock()) + ) +} diff --git a/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll b/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll index 30a8ffc3a12..e67a2d1aa21 100644 --- a/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll +++ b/java/ql/lib/semmle/code/java/security/CleartextStorageSharedPrefsQuery.qll @@ -51,7 +51,7 @@ private predicate sharedPreferencesInput(DataFlow::Node editor, Expr input) { exists(MethodAccess m | m.getMethod() instanceof PutSharedPreferenceMethod and input = m.getArgument(1) and - editor.asExpr() = m.getQualifier() + editor.asExpr() = m.getQualifier().getUnderlyingExpr() ) } @@ -61,7 +61,7 @@ private predicate sharedPreferencesInput(DataFlow::Node editor, Expr input) { */ private predicate sharedPreferencesStore(DataFlow::Node editor, MethodAccess m) { m.getMethod() instanceof StoreSharedPreferenceMethod and - editor.asExpr() = m.getQualifier() + editor.asExpr() = m.getQualifier().getUnderlyingExpr() } /** Flow from `SharedPreferences.Editor` to either a setter or a store method. */ diff --git a/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll b/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll index 176b093b68a..ba162ede986 100644 --- a/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll +++ b/java/ql/lib/semmle/code/java/security/UnsafeAndroidAccess.qll @@ -75,6 +75,8 @@ private predicate webViewLoadUrl(Argument urlArg, WebViewRef webview) { loadUrl.getArgument(0) = urlArg and loadUrl.getMethod() instanceof WebViewLoadUrlMethod | + webview.getAnAccess() = DataFlow::exprNode(loadUrl.getQualifier().getUnderlyingExpr()) + or webview.getAnAccess() = DataFlow::getInstanceArgument(loadUrl) or // `webview` is received as a parameter of an event method in a custom `WebViewClient`, @@ -82,8 +84,9 @@ private predicate webViewLoadUrl(Argument urlArg, WebViewRef webview) { exists(WebViewClientEventMethod eventMethod, MethodAccess setWebClient | setWebClient.getMethod() instanceof WebViewSetWebViewClientMethod and setWebClient.getArgument(0).getType() = eventMethod.getDeclaringType() and - loadUrl.getQualifier() = eventMethod.getWebViewParameter().getAnAccess() + loadUrl.getQualifier().getUnderlyingExpr() = eventMethod.getWebViewParameter().getAnAccess() | + webview.getAnAccess() = DataFlow::exprNode(setWebClient.getQualifier().getUnderlyingExpr()) or webview.getAnAccess() = DataFlow::getInstanceArgument(setWebClient) ) ) diff --git a/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/old.dbscheme b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/old.dbscheme new file mode 100644 index 00000000000..cf58c7d9b1f --- /dev/null +++ b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/old.dbscheme @@ -0,0 +1,1228 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * 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: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#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 + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int 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( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float 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`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + 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 +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int 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 +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar ; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * 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; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) diff --git a/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/semmlecode.dbscheme b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/semmlecode.dbscheme new file mode 100755 index 00000000000..81ccfabe82e --- /dev/null +++ b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/semmlecode.dbscheme @@ -0,0 +1,1236 @@ +/** + * An invocation of the compiler. Note that more than one file may be + * compiled per invocation. For example, this command compiles three + * source files: + * + * javac A.java B.java C.java + * + * 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: + * + * javac A.java B.java C.java + */ + unique int id : @compilation, + int kind: int ref, + string cwd : string ref, + string name : string ref +); + +case @compilation.kind of + 1 = @javacompilation +| 2 = @kotlincompilation +; + +compilation_started( + int id : @compilation ref +) + +/** + * The arguments that were passed to the extractor for a compiler + * invocation. If `id` is for the compiler invocation + * + * javac A.java B.java C.java + * + * then typically there will be rows for + * + * num | arg + * --- | --- + * 0 | *path to extractor* + * 1 | `--javac-args` + * 2 | A.java + * 3 | B.java + * 4 | C.java + */ +#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 + * + * javac A.java B.java C.java + * + * then there will be rows for + * + * num | arg + * --- | --- + * 0 | A.java + * 1 | B.java + * 2 | C.java + */ +#keyset[id, num] +compilation_compiling_files( + int id : @compilation ref, + int num : int ref, + int file : @file ref +); + +/** + * For each file recorded in `compilation_compiling_files`, + * there will be a corresponding row in + * `compilation_compiling_files_completed` once extraction + * of that file is complete. The `result` will indicate the + * extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +#keyset[id, num] +compilation_compiling_files_completed( + int id : @compilation ref, + int num : int ref, + int result : int 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( + unique int diagnostic : @diagnostic ref, + int compilation : @compilation ref, + int file_number : int ref, + int file_number_diagnostic_number : int ref +); + +/** + * The `cpu_seconds` and `elapsed_seconds` are the CPU time and elapsed + * time (respectively) that the original compilation (not the extraction) + * took for compiler invocation `id`. + */ +compilation_compiler_times( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float 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`. + * The `result` will indicate the extraction result: + * + * 0: Successfully extracted + * 1: Errors were encountered, but extraction recovered + * 2: Errors were encountered, and extraction could not recover + */ +compilation_finished( + unique int id : @compilation ref, + float cpu_seconds : float ref, + float elapsed_seconds : float ref, + int result : int ref +); + +diagnostics( + unique int id: @diagnostic, + string generated_by: string ref, // TODO: Sync this with the other languages? + 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 +); + +/* + * External artifacts + */ + +externalData( + int id : @externalDataElement, + string path : string ref, + int column: int ref, + string value : string ref +); + +snapshotDate( + unique date snapshotDate : date ref +); + +sourceLocationPrefix( + string prefix : string ref +); + +/* + * Duplicate code + */ + +duplicateCode( + unique int id : @duplication, + string relativePath : string ref, + int equivClass : int ref +); + +similarCode( + unique int id : @similarity, + string relativePath : string ref, + int equivClass : int ref +); + +@duplication_or_similarity = @duplication | @similarity + +tokens( + int id : @duplication_or_similarity ref, + int offset : int ref, + int beginLine : int ref, + int beginColumn : int ref, + int endLine : int ref, + int endColumn : int ref +); + +/* + * SMAP + */ + +smap_header( + int outputFileId: @file ref, + string outputFilename: string ref, + string defaultStratum: string ref +); + +smap_files( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + string inputFileName: string ref, + int inputFileId: @file ref +); + +smap_lines( + int outputFileId: @file ref, + string stratum: string ref, + int inputFileNum: int ref, + int inputStartLine: int ref, + int inputLineCount: int ref, + int outputStartLine: int ref, + int outputLineIncrement: int ref +); + +/* + * Locations and files + */ + +@location = @location_default ; + +locations_default( + unique int id: @location_default, + int file: @file ref, + int beginLine: int ref, + int beginColumn: int ref, + int endLine: int ref, + int endColumn: int ref +); + +hasLocation( + int locatableid: @locatable ref, + int id: @location ref +); + +@sourceline = @locatable ; + +#keyset[element_id] +numlines( + int element_id: @sourceline ref, + int num_lines: int ref, + int num_code: int ref, + int num_comment: int 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 +); + +/* + * Java + */ + +cupackage( + unique int id: @file ref, + int packageid: @package ref +); + +#keyset[fileid,keyName] +jarManifestMain( + int fileid: @file ref, + string keyName: string ref, + string value: string ref +); + +#keyset[fileid,entryName,keyName] +jarManifestEntries( + int fileid: @file ref, + string entryName: string ref, + string keyName: string ref, + string value: string ref +); + +packages( + unique int id: @package, + string nodeName: string ref +); + +primitives( + unique int id: @primitive, + string nodeName: string ref +); + +modifiers( + unique int id: @modifier, + string nodeName: string ref +); + +/** + * An errortype is used when the extractor is unable to extract a type + * correctly for some reason. + */ +error_type( + unique int id: @errortype +); + +classes( + unique int id: @class, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @class ref +); + +file_class( + int id: @class ref +); + +class_object( + unique int id: @class ref, + unique int instance: @field ref +); + +type_companion_object( + unique int id: @classorinterface ref, + unique int instance: @field ref, + unique int companion_object: @class ref +); + +kt_nullable_types( + unique int id: @kt_nullable_type, + int classid: @reftype ref +) + +kt_notnull_types( + unique int id: @kt_notnull_type, + int classid: @reftype ref +) + +kt_type_alias( + unique int id: @kt_type_alias, + string name: string ref, + int kttypeid: @kt_type ref +) + +@kt_type = @kt_nullable_type | @kt_notnull_type + +isRecord( + unique int id: @class ref +); + +interfaces( + unique int id: @interface, + string nodeName: string ref, + int parentid: @package ref, + int sourceid: @interface ref +); + +fielddecls( + unique int id: @fielddecl, + int parentid: @reftype ref +); + +#keyset[fieldId] #keyset[fieldDeclId,pos] +fieldDeclaredIn( + int fieldId: @field ref, + int fieldDeclId: @fielddecl ref, + int pos: int ref +); + +fields( + unique int id: @field, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @field ref +); + +fieldsKotlinType( + unique int id: @field ref, + int kttypeid: @kt_type ref +); + +constrs( + unique int id: @constructor, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @constructor ref +); + +constrsKotlinType( + unique int id: @constructor ref, + int kttypeid: @kt_type ref +); + +methods( + unique int id: @method, + string nodeName: string ref, + string signature: string ref, + int typeid: @type ref, + int parentid: @reftype ref, + int sourceid: @method ref +); + +methodsKotlinType( + unique int id: @method ref, + int kttypeid: @kt_type ref +); + +#keyset[parentid,pos] +params( + unique int id: @param, + int typeid: @type ref, + int pos: int ref, + int parentid: @callable ref, + int sourceid: @param ref +); + +paramsKotlinType( + unique int id: @param ref, + int kttypeid: @kt_type ref +); + +paramName( + unique int id: @param ref, + string nodeName: string ref +); + +isVarargsParam( + int param: @param ref +); + +exceptions( + unique int id: @exception, + int typeid: @type ref, + int parentid: @callable ref +); + +isAnnotType( + int interfaceid: @interface ref +); + +isAnnotElem( + int methodid: @method ref +); + +annotValue( + int parentid: @annotation ref, + int id2: @method ref, + unique int value: @expr ref +); + +isEnumType( + int classid: @class ref +); + +isEnumConst( + int fieldid: @field ref +); + +#keyset[parentid,pos] +typeVars( + unique int id: @typevariable, + string nodeName: string ref, + int pos: int ref, + int kind: int ref, // deprecated + int parentid: @classorinterfaceorcallable ref +); + +wildcards( + unique int id: @wildcard, + string nodeName: string ref, + int kind: int ref +); + +#keyset[parentid,pos] +typeBounds( + unique int id: @typebound, + int typeid: @reftype ref, + int pos: int ref, + int parentid: @boundedtype ref +); + +#keyset[parentid,pos] +typeArgs( + int argumentid: @reftype ref, + int pos: int ref, + int parentid: @classorinterfaceorcallable ref +); + +isParameterized( + int memberid: @member ref +); + +isRaw( + int memberid: @member ref +); + +erasure( + unique int memberid: @member ref, + int erasureid: @member ref +); + +#keyset[classid] #keyset[parent] +isAnonymClass( + int classid: @class ref, + int parent: @classinstancexpr ref +); + +#keyset[typeid] #keyset[parent] +isLocalClassOrInterface( + int typeid: @classorinterface ref, + int parent: @localtypedeclstmt ref +); + +isDefConstr( + int constructorid: @constructor ref +); + +#keyset[exprId] +lambdaKind( + int exprId: @lambdaexpr ref, + int bodyKind: int ref +); + +arrays( + unique int id: @array, + string nodeName: string ref, + int elementtypeid: @type ref, + int dimension: int ref, + int componenttypeid: @type ref +); + +enclInReftype( + unique int child: @reftype ref, + int parent: @reftype ref +); + +extendsReftype( + int id1: @reftype ref, + int id2: @classorinterface ref +); + +implInterface( + int id1: @classorarray ref, + int id2: @interface ref +); + +permits( + int id1: @classorinterface ref, + int id2: @classorinterface ref +); + +hasModifier( + int id1: @modifiable ref, + int id2: @modifier ref +); + +imports( + unique int id: @import, + int holder: @classorinterfaceorpackage ref, + string name: string ref, + int kind: int ref +); + +#keyset[parent,idx] +stmts( + unique int id: @stmt, + int kind: int ref, + int parent: @stmtparent ref, + int idx: int ref, + int bodydecl: @callable ref +); + +@stmtparent = @callable | @stmt | @switchexpr | @whenexpr| @stmtexpr; + +case @stmt.kind of + 0 = @block +| 1 = @ifstmt +| 2 = @forstmt +| 3 = @enhancedforstmt +| 4 = @whilestmt +| 5 = @dostmt +| 6 = @trystmt +| 7 = @switchstmt +| 8 = @synchronizedstmt +| 9 = @returnstmt +| 10 = @throwstmt +| 11 = @breakstmt +| 12 = @continuestmt +| 13 = @emptystmt +| 14 = @exprstmt +| 15 = @labeledstmt +| 16 = @assertstmt +| 17 = @localvariabledeclstmt +| 18 = @localtypedeclstmt +| 19 = @constructorinvocationstmt +| 20 = @superconstructorinvocationstmt +| 21 = @case +| 22 = @catchclause +| 23 = @yieldstmt +| 24 = @errorstmt +| 25 = @whenbranch +; + +#keyset[parent,idx] +exprs( + unique int id: @expr, + int kind: int ref, + int typeid: @type ref, + int parent: @exprparent ref, + int idx: int ref +); + +exprsKotlinType( + unique int id: @expr ref, + int kttypeid: @kt_type ref +); + +callableEnclosingExpr( + unique int id: @expr ref, + int callable_id: @callable ref +); + +statementEnclosingExpr( + unique int id: @expr ref, + int statement_id: @stmt ref +); + +isParenthesized( + unique int id: @expr ref, + int parentheses: int ref +); + +case @expr.kind of + 1 = @arrayaccess +| 2 = @arraycreationexpr +| 3 = @arrayinit +| 4 = @assignexpr +| 5 = @assignaddexpr +| 6 = @assignsubexpr +| 7 = @assignmulexpr +| 8 = @assigndivexpr +| 9 = @assignremexpr +| 10 = @assignandexpr +| 11 = @assignorexpr +| 12 = @assignxorexpr +| 13 = @assignlshiftexpr +| 14 = @assignrshiftexpr +| 15 = @assignurshiftexpr +| 16 = @booleanliteral +| 17 = @integerliteral +| 18 = @longliteral +| 19 = @floatingpointliteral +| 20 = @doubleliteral +| 21 = @characterliteral +| 22 = @stringliteral +| 23 = @nullliteral +| 24 = @mulexpr +| 25 = @divexpr +| 26 = @remexpr +| 27 = @addexpr +| 28 = @subexpr +| 29 = @lshiftexpr +| 30 = @rshiftexpr +| 31 = @urshiftexpr +| 32 = @andbitexpr +| 33 = @orbitexpr +| 34 = @xorbitexpr +| 35 = @andlogicalexpr +| 36 = @orlogicalexpr +| 37 = @ltexpr +| 38 = @gtexpr +| 39 = @leexpr +| 40 = @geexpr +| 41 = @eqexpr +| 42 = @neexpr +| 43 = @postincexpr +| 44 = @postdecexpr +| 45 = @preincexpr +| 46 = @predecexpr +| 47 = @minusexpr +| 48 = @plusexpr +| 49 = @bitnotexpr +| 50 = @lognotexpr +| 51 = @castexpr +| 52 = @newexpr +| 53 = @conditionalexpr +| 54 = @parexpr // deprecated +| 55 = @instanceofexpr +| 56 = @localvariabledeclexpr +| 57 = @typeliteral +| 58 = @thisaccess +| 59 = @superaccess +| 60 = @varaccess +| 61 = @methodaccess +| 62 = @unannotatedtypeaccess +| 63 = @arraytypeaccess +| 64 = @packageaccess +| 65 = @wildcardtypeaccess +| 66 = @declannotation +| 67 = @uniontypeaccess +| 68 = @lambdaexpr +| 69 = @memberref +| 70 = @annotatedtypeaccess +| 71 = @typeannotation +| 72 = @intersectiontypeaccess +| 73 = @switchexpr +| 74 = @errorexpr +| 75 = @whenexpr +| 76 = @getclassexpr +| 77 = @safecastexpr +| 78 = @implicitcastexpr +| 79 = @implicitnotnullexpr +| 80 = @implicitcoerciontounitexpr +| 81 = @notinstanceofexpr +| 82 = @stmtexpr +| 83 = @stringtemplateexpr +| 84 = @notnullexpr +| 85 = @unsafecoerceexpr +| 86 = @valueeqexpr +| 87 = @valueneexpr +| 88 = @propertyref +; + +/** Holds if this `when` expression was written as an `if` expression. */ +when_if(unique int id: @whenexpr ref); + +/** Holds if this `when` branch was written as an `else` branch. */ +when_branch_else(unique int id: @whenbranch ref); + +@classinstancexpr = @newexpr | @lambdaexpr | @memberref | @propertyref + +@annotation = @declannotation | @typeannotation +@typeaccess = @unannotatedtypeaccess | @annotatedtypeaccess + +@assignment = @assignexpr + | @assignop; + +@unaryassignment = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr; + +@assignop = @assignaddexpr + | @assignsubexpr + | @assignmulexpr + | @assigndivexpr + | @assignremexpr + | @assignandexpr + | @assignorexpr + | @assignxorexpr + | @assignlshiftexpr + | @assignrshiftexpr + | @assignurshiftexpr; + +@literal = @booleanliteral + | @integerliteral + | @longliteral + | @floatingpointliteral + | @doubleliteral + | @characterliteral + | @stringliteral + | @nullliteral; + +@binaryexpr = @mulexpr + | @divexpr + | @remexpr + | @addexpr + | @subexpr + | @lshiftexpr + | @rshiftexpr + | @urshiftexpr + | @andbitexpr + | @orbitexpr + | @xorbitexpr + | @andlogicalexpr + | @orlogicalexpr + | @ltexpr + | @gtexpr + | @leexpr + | @geexpr + | @eqexpr + | @neexpr + | @valueeqexpr + | @valueneexpr; + +@unaryexpr = @postincexpr + | @postdecexpr + | @preincexpr + | @predecexpr + | @minusexpr + | @plusexpr + | @bitnotexpr + | @lognotexpr + | @notnullexpr; + +@caller = @classinstancexpr + | @methodaccess + | @constructorinvocationstmt + | @superconstructorinvocationstmt; + +callableBinding( + unique int callerid: @caller ref, + int callee: @callable ref +); + +memberRefBinding( + unique int id: @expr ref, + int callable: @callable ref +); + +propertyRefGetBinding( + unique int id: @expr ref, + int getter: @callable ref +); + +propertyRefFieldBinding( + unique int id: @expr ref, + int field: @field ref +); + +propertyRefSetBinding( + unique int id: @expr ref, + int setter: @callable ref +); + +@exprparent = @stmt | @expr | @whenbranch | @callable | @field | @fielddecl | @class | @interface | @param | @localvar | @typevariable; + +variableBinding( + unique int expr: @varaccess ref, + int variable: @variable ref +); + +@variable = @localscopevariable | @field; + +@localscopevariable = @localvar | @param; + +localvars( + unique int id: @localvar, + string nodeName: string ref, + int typeid: @type ref, + int parentid: @localvariabledeclexpr ref +); + +localvarsKotlinType( + unique int id: @localvar ref, + int kttypeid: @kt_type ref +); + +@namedexprorstmt = @breakstmt + | @continuestmt + | @labeledstmt + | @literal; + +namestrings( + string name: string ref, + string value: string ref, + unique int parent: @namedexprorstmt ref +); + +/* + * Modules + */ + +#keyset[name] +modules( + unique int id: @module, + string name: string ref +); + +isOpen( + int id: @module ref +); + +#keyset[fileId] +cumodule( + int fileId: @file ref, + int moduleId: @module ref +); + +@directive = @requires + | @exports + | @opens + | @uses + | @provides + +#keyset[directive] +directives( + int id: @module ref, + int directive: @directive ref +); + +requires( + unique int id: @requires, + int target: @module ref +); + +isTransitive( + int id: @requires ref +); + +isStatic( + int id: @requires ref +); + +exports( + unique int id: @exports, + int target: @package ref +); + +exportsTo( + int id: @exports ref, + int target: @module ref +); + +opens( + unique int id: @opens, + int target: @package ref +); + +opensTo( + int id: @opens ref, + int target: @module ref +); + +uses( + unique int id: @uses, + string serviceInterface: string ref +); + +provides( + unique int id: @provides, + string serviceInterface: string ref +); + +providesWith( + int id: @provides ref, + string serviceImpl: string ref +); + +/* + * Javadoc + */ + +javadoc( + unique int id: @javadoc +); + +isNormalComment( + int commentid : @javadoc ref +); + +isEolComment( + int commentid : @javadoc ref +); + +hasJavadoc( + int documentableid: @member ref, + int javadocid: @javadoc ref +); + +#keyset[parentid,idx] +javadocTag( + unique int id: @javadocTag, + string name: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +#keyset[parentid,idx] +javadocText( + unique int id: @javadocText, + string text: string ref, + int parentid: @javadocParent ref, + int idx: int ref +); + +@javadocParent = @javadoc | @javadocTag; +@javadocElement = @javadocTag | @javadocText; + +@classorinterface = @interface | @class; +@classorinterfaceorpackage = @classorinterface | @package; +@classorinterfaceorcallable = @classorinterface | @callable; +@boundedtype = @typevariable | @wildcard; +@reftype = @classorinterface | @array | @boundedtype | @errortype; +@classorarray = @class | @array; +@type = @primitive | @reftype; +@callable = @method | @constructor; + +/** A program element that has a name. */ +@element = @package | @modifier | @annotation | @errortype | + @locatableElement; + +@locatableElement = @file | @primitive | @class | @interface | @method | @constructor | @param | @exception | @field | + @boundedtype | @array | @localvar | @expr | @stmt | @import | @fielddecl | @kt_type | @kt_type_alias | + @kt_property; + +@modifiable = @member_modifiable| @param | @localvar ; + +@member_modifiable = @class | @interface | @method | @constructor | @field | @kt_property; + +@member = @method | @constructor | @field | @reftype ; + +/** A program element that has a location. */ +@locatable = @typebound | @javadoc | @javadocTag | @javadocText | @xmllocatable | @ktcomment | + @locatableElement; + +@top = @element | @locatable | @folder; + +/* + * 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; + +/* + * configuration files with key value pairs + */ + +configs( + unique int id: @config +); + +configNames( + unique int id: @configName, + int config: @config ref, + string name: string ref +); + +configValues( + unique int id: @configValue, + int config: @config ref, + string value: string ref +); + +configLocations( + int locatable: @configLocatable ref, + int location: @location_default ref +); + +@configLocatable = @config | @configName | @configValue; + +ktComments( + unique int id: @ktcomment, + int kind: int ref, + string text : string ref +) + +ktCommentSections( + unique int id: @ktcommentsection, + int comment: @ktcomment ref, + string content : string ref +) + +ktCommentSectionNames( + unique int id: @ktcommentsection ref, + string name : string ref +) + +ktCommentSectionSubjectNames( + unique int id: @ktcommentsection ref, + string subjectname : string ref +) + +#keyset[id, owner] +ktCommentOwners( + int id: @ktcomment ref, + int owner: @top ref +) + +ktExtensionFunctions( + unique int id: @method ref, + int typeid: @type ref, + int kttypeid: @kt_type ref +) + +ktProperties( + unique int id: @kt_property, + string nodeName: string ref +) + +ktPropertyGetters( + unique int id: @kt_property ref, + int getter: @method ref +) + +ktPropertySetters( + unique int id: @kt_property ref, + int setter: @method ref +) + +ktPropertyBackingFields( + unique int id: @kt_property ref, + int backingField: @field ref +) + +ktSyntheticBody( + unique int id: @callable ref, + int kind: int ref + // 1: ENUM_VALUES + // 2: ENUM_VALUEOF +) + +ktLocalFunction( + unique int id: @method ref +) + +ktInitializerAssignment( + unique int id: @assignexpr ref +) + +ktPropertyDelegates( + unique int id: @kt_property ref, + unique int variableId: @variable ref +) + +/** + * If `id` is a compiler generated element, then the kind indicates the + * reason that the compiler generated it. + * See `Element.compilerGeneratedReason()` for an explanation of what + * each `kind` means. + */ +compiler_generated( + unique int id: @element ref, + int kind: int ref +) + +ktFunctionOriginalNames( + unique int id: @method ref, + string name: string ref +) diff --git a/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/upgrade.properties b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/upgrade.properties new file mode 100644 index 00000000000..25c651f591b --- /dev/null +++ b/java/ql/lib/upgrades/cf58c7d9b1fa1eae9cdc20ce8f157c140ac0c3de/upgrade.properties @@ -0,0 +1,2 @@ +description: Add errortype +compatibility: full diff --git a/java/ql/src/CHANGELOG.md b/java/ql/src/CHANGELOG.md index 1f8a00fb1ff..33ae45fbb9f 100644 --- a/java/ql/src/CHANGELOG.md +++ b/java/ql/src/CHANGELOG.md @@ -1,3 +1,17 @@ +## 0.3.1 + +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/java-all` package. + +### New Queries + +* A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added. + This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered + to receive system intents. + ## 0.2.0 ### Minor Analysis Improvements diff --git a/java/ql/src/Likely Bugs/Collections/ContainsTypeMismatch.ql b/java/ql/src/Likely Bugs/Collections/ContainsTypeMismatch.ql index b34830c3537..52d790e0e71 100644 --- a/java/ql/src/Likely Bugs/Collections/ContainsTypeMismatch.ql +++ b/java/ql/src/Likely Bugs/Collections/ContainsTypeMismatch.ql @@ -118,7 +118,7 @@ class MismatchedContainerAccess extends MethodAccess { containerAccess(package, type, p, this.getCallee().getSignature(), i) | t = this.getCallee().getDeclaringType() and - t.getAnAncestor().getSourceDeclaration() = g and + t.getASourceSupertype*().getSourceDeclaration() = g and g.hasQualifiedName(package, type) and indirectlyInstantiates(t, g, p, result) ) @@ -139,7 +139,7 @@ from MismatchedContainerAccess ma, RefType typearg, RefType argtype, int idx where typearg = ma.getReceiverElementType(idx).getSourceDeclaration() and argtype = ma.getArgumentType(idx) and - not haveIntersection(typearg, argtype) + notHaveIntersection(typearg, argtype) select ma.getArgument(idx), "Actual argument type '" + argtype.getName() + "'" + " is incompatible with expected argument type '" + typearg.getName() + "'." diff --git a/java/ql/src/Likely Bugs/Collections/RemoveTypeMismatch.ql b/java/ql/src/Likely Bugs/Collections/RemoveTypeMismatch.ql index 8fa467c2d8a..076ccc12240 100644 --- a/java/ql/src/Likely Bugs/Collections/RemoveTypeMismatch.ql +++ b/java/ql/src/Likely Bugs/Collections/RemoveTypeMismatch.ql @@ -88,7 +88,7 @@ class MismatchedContainerModification extends MethodAccess { containerModification(package, type, p, this.getCallee().getSignature(), i) | t = this.getCallee().getDeclaringType() and - t.getAnAncestor().getSourceDeclaration() = g and + t.getASourceSupertype*().getSourceDeclaration() = g and g.hasQualifiedName(package, type) and indirectlyInstantiates(t, g, p, result) ) @@ -109,7 +109,7 @@ from MismatchedContainerModification ma, RefType elementtype, RefType argtype, i where elementtype = ma.getReceiverElementType(idx).getSourceDeclaration() and argtype = ma.getArgumentType(idx) and - not haveIntersection(elementtype, argtype) + notHaveIntersection(elementtype, argtype) select ma.getArgument(idx), "Actual argument type '" + argtype.getName() + "'" + " is incompatible with expected argument type '" + elementtype.getName() + "'." diff --git a/java/ql/src/Likely Bugs/Comparison/IncomparableEquals.ql b/java/ql/src/Likely Bugs/Comparison/IncomparableEquals.ql index c083c80c21d..d98fc77af38 100644 --- a/java/ql/src/Likely Bugs/Comparison/IncomparableEquals.ql +++ b/java/ql/src/Likely Bugs/Comparison/IncomparableEquals.ql @@ -57,7 +57,7 @@ where else recvtp = ma.getMethod().getDeclaringType() ) and argtp = ma.getArgumentType() and - not haveIntersection(recvtp, argtp) + notHaveIntersection(recvtp, argtp) select ma, "Call to equals() comparing incomparable types " + recvtp.getName() + " and " + argtp.getName() + "." diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql index 306b835b98b..671e9b00b4d 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql @@ -15,6 +15,7 @@ import java import semmle.code.java.dataflow.FlowSources +private import semmle.code.java.dataflow.ExternalFlow import semmle.code.java.security.PathCreation import DataFlow::PathGraph import TaintedPathCommon @@ -34,7 +35,12 @@ class TaintedPathConfig extends TaintTracking::Configuration { override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource } override predicate isSink(DataFlow::Node sink) { - exists(Expr e | e = sink.asExpr() | e = any(PathCreation p).getAnInput() and not guarded(e)) + ( + sink.asExpr() = any(PathCreation p).getAnInput() + or + sinkNode(sink, "create-file") + ) and + not guarded(sink.asExpr()) } override predicate isSanitizer(DataFlow::Node node) { @@ -44,9 +50,21 @@ class TaintedPathConfig extends TaintTracking::Configuration { } } -from DataFlow::PathNode source, DataFlow::PathNode sink, PathCreation p, TaintedPathConfig conf -where - sink.getNode().asExpr() = p.getAnInput() and - conf.hasFlowPath(source, sink) -select p, source, sink, "$@ flows to here and is used in a path.", source.getNode(), - "User-provided value" +/** + * Gets the data-flow node at which to report a path ending at `sink`. + * + * Previously this query flagged alerts exclusively at `PathCreation` sites, + * so to avoid perturbing existing alerts, where a `PathCreation` exists we + * continue to report there; otherwise we report directly at `sink`. + */ +DataFlow::Node getReportingNode(DataFlow::Node sink) { + any(TaintedPathConfig c).hasFlowTo(sink) and + if exists(PathCreation pc | pc.getAnInput() = sink.asExpr()) + then result.asExpr() = any(PathCreation pc | pc.getAnInput() = sink.asExpr()) + else result = sink +} + +from DataFlow::PathNode source, DataFlow::PathNode sink, TaintedPathConfig conf +where conf.hasFlowPath(source, sink) +select getReportingNode(sink.getNode()), source, sink, "$@ flows to here and is used in a path.", + source.getNode(), "User-provided value" diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.java b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.java new file mode 100644 index 00000000000..358800c5746 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.java @@ -0,0 +1,22 @@ +class Bad extends WebViewClient { + // BAD: All certificates are trusted. + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { // $hasResult + handler.proceed(); + } +} + +class Good extends WebViewClient { + PublicKey myPubKey = ...; + + // GOOD: Only certificates signed by a certain public key are trusted. + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { // $hasResult + try { + X509Certificate cert = error.getCertificate().getX509Certificate(); + cert.verify(this.myPubKey); + handler.proceed(); + } + catch (CertificateException|NoSuchAlgorithmException|InvalidKeyException|NoSuchProviderException|SignatureException e) { + handler.cancel(); + } + } +} \ No newline at end of file diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp new file mode 100644 index 00000000000..2a8d3b58a4c --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.qhelp @@ -0,0 +1,46 @@ + + + +

    +If the onReceivedSslError method of an Android WebViewClient always calls proceed on the given SslErrorHandler, it trusts any certificate. +This allows an attacker to perform a machine-in-the-middle attack against the application, therefore breaking any security Transport Layer Security (TLS) gives. +

    + +

    +An attack might look like this: +

    + +
      +
    1. The vulnerable application connects to https://example.com.
    2. +
    3. The attacker intercepts this connection and presents a valid, self-signed certificate for https://example.com.
    4. +
    5. The vulnerable application calls the onReceivedSslError method to check whether it should trust the certificate.
    6. +
    7. The onReceivedSslError method of your WebViewClient calls SslErrorHandler.proceed.
    8. +
    9. The vulnerable application accepts the certificate and proceeds with the connection since your WevViewClient trusted it by proceeding.
    10. +
    11. The attacker can now read the data your application sends to https://example.com and/or alter its replies while the application thinks the connection is secure.
    12. +
    +
    + + +

    +Do not use a call SslerrorHandler.proceed unconditionally. +If you have to use a self-signed certificate, only accept that certificate, not all certificates. +

    + +
    + + +

    +In the first (bad) example, the WebViewClient trusts all certificates by always calling SslErrorHandler.proceed. +In the second (good) example, only certificates signed by a certain public key are accepted. +

    + +
    + + +
  • +WebViewClient.onReceivedSslError documentation. +
  • +
    +
    diff --git a/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql new file mode 100644 index 00000000000..aac3a99be4c --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-295/ImproperWebViewCertificateValidation.ql @@ -0,0 +1,18 @@ +/** + * @name Android `WebView` that accepts all certificates + * @description Trusting all certificates allows an attacker to perform a machine-in-the-middle attack. + * @kind problem + * @problem.severity error + * @security-severity 7.5 + * @precision high + * @id java/improper-webview-certificate-validation + * @tags security + * external/cwe/cwe-295 + */ + +import java +import semmle.code.java.security.AndroidWebViewCertificateValidationQuery + +from OnReceivedSslErrorMethod m +where trustsAllCerts(m) +select m, "This handler accepts all SSL certificates." diff --git a/java/ql/src/Telemetry/ExternalApi.qll b/java/ql/src/Telemetry/ExternalApi.qll index b839ca15c6d..eaf88555444 100644 --- a/java/ql/src/Telemetry/ExternalApi.qll +++ b/java/ql/src/Telemetry/ExternalApi.qll @@ -73,7 +73,7 @@ class ExternalApi extends Callable { TaintTracking::localAdditionalTaintStep(this.getAnInput(), _) } - /** Holds if this API is is a constructor without parameters. */ + /** Holds if this API is a constructor without parameters. */ private predicate isParameterlessConstructor() { this instanceof Constructor and this.getNumberOfParameters() = 0 } diff --git a/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql b/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql index 726c2453f65..e3ac1384243 100644 --- a/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql +++ b/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverloading.ql @@ -60,13 +60,43 @@ private predicate candidateMethod(RefType t, Method m, string name, int numParam not whitelist(name) } -pragma[inline] -private predicate potentiallyConfusingTypes(Type a, Type b) { - exists(RefType commonSubtype | hasSubtypeOrInstantiation*(a, commonSubtype) | - hasSubtypeOrInstantiation*(b, commonSubtype) +predicate paramTypePair(Type t1, Type t2) { + exists(Method n, Method m, int i | + overloadedMethodsMostSpecific(n, m) and + t1 = n.getParameterType(i) and + t2 = m.getParameterType(i) ) +} + +// handle simple cases separately +predicate potentiallyConfusingTypesSimple(Type t1, Type t2) { + paramTypePair(t1, t2) and + ( + t1 = t2 + or + t1 instanceof TypeObject and t2 instanceof RefType + or + t2 instanceof TypeObject and t1 instanceof RefType + or + confusingPrimitiveBoxedTypes(t1, t2) + ) +} + +// check erased types first +predicate potentiallyConfusingTypesRefTypes(RefType t1, RefType t2) { + paramTypePair(t1, t2) and + not potentiallyConfusingTypesSimple(t1, t2) and + haveIntersection(t1, t2) +} + +// then check hasSubtypeOrInstantiation +predicate potentiallyConfusingTypes(Type t1, Type t2) { + potentiallyConfusingTypesSimple(t1, t2) or - confusingPrimitiveBoxedTypes(a, b) + potentiallyConfusingTypesRefTypes(t1, t2) and + exists(RefType commonSubtype | hasSubtypeOrInstantiation*(t1, commonSubtype) | + hasSubtypeOrInstantiation*(t2, commonSubtype) + ) } private predicate hasSubtypeOrInstantiation(RefType t, RefType sub) { diff --git a/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql b/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql index 79f5f2cf473..a9f99658f94 100644 --- a/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql +++ b/java/ql/src/Violations of Best Practice/Naming Conventions/SameNameAsSuper.ql @@ -16,5 +16,5 @@ from RefType sub, RefType sup where sub.fromSource() and sup = sub.getASupertype() and - sub.getName() = sup.getName() + pragma[only_bind_out](sub.getName()) = pragma[only_bind_out](sup.getName()) select sub, sub.getName() + " has the same name as its supertype $@.", sup, sup.getQualifiedName() diff --git a/java/ql/src/change-notes/2022-04-27-intent-verification.md b/java/ql/src/change-notes/2022-04-27-intent-verification.md deleted file mode 100644 index e5e0e287753..00000000000 --- a/java/ql/src/change-notes/2022-04-27-intent-verification.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -category: newQuery ---- -* A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added. -This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered -to receive system intents. \ No newline at end of file diff --git a/java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md b/java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md new file mode 100644 index 00000000000..3e80487d772 --- /dev/null +++ b/java/ql/src/change-notes/2022-06-22-improper-webview-certificate-validation.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* A new query "Android `WebView` that accepts all certificates" (`java/improper-webview-certificate-validation`) has been added. This query finds implementations of `WebViewClient`s that accept all certificates in the case of an SSL error. \ No newline at end of file diff --git a/java/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/java/ql/src/change-notes/2022-06-29-move-contextual-queries.md deleted file mode 100644 index 02ff5b6d59c..00000000000 --- a/java/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: breaking ---- -* Contextual queries and the query libraries they depend on have been moved to the `codeql/java-all` package. diff --git a/java/ql/src/change-notes/2022-08-03-tainted-path-mad.md b/java/ql/src/change-notes/2022-08-03-tainted-path-mad.md new file mode 100644 index 00000000000..6f70a8f69e1 --- /dev/null +++ b/java/ql/src/change-notes/2022-08-03-tainted-path-mad.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The query `java/path-injection` now recognises vulnerable APIs defined using the `SinkModelCsv` class with the `create-file` type. Out of the box this includes Apache Commons-IO functions, as well as any user-defined sinks. diff --git a/java/ql/src/change-notes/released/0.3.0.md b/java/ql/src/change-notes/released/0.3.0.md new file mode 100644 index 00000000000..d91c653a471 --- /dev/null +++ b/java/ql/src/change-notes/released/0.3.0.md @@ -0,0 +1,11 @@ +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/java-all` package. + +### New Queries + +* A new query "Improper verification of intent by broadcast receiver" (`java/improper-intent-verification`) has been added. + This query finds instances of Android `BroadcastReceiver`s that don't verify the action string of received intents when registered + to receive system intents. diff --git a/java/ql/src/change-notes/released/0.3.1.md b/java/ql/src/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..2b0719929a1 --- /dev/null +++ b/java/ql/src/change-notes/released/0.3.1.md @@ -0,0 +1 @@ +## 0.3.1 diff --git a/java/ql/src/codeql-pack.release.yml b/java/ql/src/codeql-pack.release.yml index 5274e27ed52..bb106b1cb63 100644 --- a/java/ql/src/codeql-pack.release.yml +++ b/java/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.1 diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java new file mode 100644 index 00000000000..83157c14251 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.java @@ -0,0 +1,46 @@ + +// BAD: Using an outdated SDK that does not support client side encryption version V2_0 +new EncryptedBlobClientBuilder() + .blobClient(blobClient) + .key(resolver.buildAsyncKeyEncryptionKey(keyid).block(), keyWrapAlgorithm) + .buildEncryptedBlobClient() + .uploadWithResponse(new BlobParallelUploadOptions(data) + .setMetadata(metadata) + .setHeaders(headers) + .setTags(tags) + .setTier(tier) + .setRequestConditions(requestConditions) + .setComputeMd5(computeMd5) + .setParallelTransferOptions(parallelTransferOptions), + timeout, context); + +// BAD: Using the deprecatedd client side encryption version V1_0 +new EncryptedBlobClientBuilder(EncryptionVersion.V1) + .blobClient(blobClient) + .key(resolver.buildAsyncKeyEncryptionKey(keyid).block(), keyWrapAlgorithm) + .buildEncryptedBlobClient() + .uploadWithResponse(new BlobParallelUploadOptions(data) + .setMetadata(metadata) + .setHeaders(headers) + .setTags(tags) + .setTier(tier) + .setRequestConditions(requestConditions) + .setComputeMd5(computeMd5) + .setParallelTransferOptions(parallelTransferOptions), + timeout, context); + + +// GOOD: Using client side encryption version V2_0 +new EncryptedBlobClientBuilder(EncryptionVersion.V2) + .blobClient(blobClient) + .key(resolver.buildAsyncKeyEncryptionKey(keyid).block(), keyWrapAlgorithm) + .buildEncryptedBlobClient() + .uploadWithResponse(new BlobParallelUploadOptions(data) + .setMetadata(metadata) + .setHeaders(headers) + .setTags(tags) + .setTier(tier) + .setRequestConditions(requestConditions) + .setComputeMd5(computeMd5) + .setParallelTransferOptions(parallelTransferOptions), + timeout, context); diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp new file mode 100644 index 00000000000..b6884aed914 --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -0,0 +1,29 @@ + + + + + +

    Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.

    +

    The Azure Storage SDK version 12.18.0 or later supports version V2 for client-side encryption. All previous versions of Azure Storage SDK only support client-side encryption V1 which is unsafe.

    + +
    + + +

    Consider switching to V2 client-side encryption.

    + +
    + + + + + + +
  • + Azure Storage Client Encryption Blog. +
  • +
  • + CVE-2022-30187 +
  • + +
    +
    diff --git a/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql new file mode 100644 index 00000000000..287a3f07a6c --- /dev/null +++ b/java/ql/src/experimental/Security/CWE/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -0,0 +1,92 @@ +/** + * @name Unsafe usage of v1 version of Azure Storage client-side encryption (CVE-2022-30187). + * @description Unsafe usage of v1 version of Azure Storage client-side encryption, please refer to http://aka.ms/azstorageclientencryptionblog + * @kind problem + * @tags security + * cryptography + * external/cwe/cwe-327 + * @id java/azure-storage/unsafe-client-side-encryption-in-use + * @problem.severity error + * @precision high + */ + +import java +import semmle.code.java.dataflow.DataFlow + +/** + * Holds if `call` is an object creation for a class `EncryptedBlobClientBuilder` + * that takes no arguments, which means that it is using V1 encryption. + */ +predicate isCreatingOutdatedAzureClientSideEncryptionObject(Call call, Class c) { + exists(string package, string type, Constructor constructor | + c.hasQualifiedName(package, type) and + c.getAConstructor() = constructor and + call.getCallee() = constructor and + ( + type = "EncryptedBlobClientBuilder" and + package = "com.azure.storage.blob.specialized.cryptography" and + constructor.hasNoParameters() + or + type = "BlobEncryptionPolicy" and package = "com.microsoft.azure.storage.blob" + ) + ) +} + +/** + * Holds if `call` is an object creation for a class `EncryptedBlobClientBuilder` + * that takes `versionArg` as the argument specifying the encryption version. + */ +predicate isCreatingAzureClientSideEncryptionObjectNewVersion(Call call, Class c, Expr versionArg) { + exists(string package, string type, Constructor constructor | + c.hasQualifiedName(package, type) and + c.getAConstructor() = constructor and + call.getCallee() = constructor and + type = "EncryptedBlobClientBuilder" and + package = "com.azure.storage.blob.specialized.cryptography" and + versionArg = call.getArgument(0) + ) +} + +/** + * A dataflow config that tracks `EncryptedBlobClientBuilder.version` argument initialization. + */ +private class EncryptedBlobClientBuilderSafeEncryptionVersionConfig extends DataFlow::Configuration { + EncryptedBlobClientBuilderSafeEncryptionVersionConfig() { + this = "EncryptedBlobClientBuilderSafeEncryptionVersionConfig" + } + + override predicate isSource(DataFlow::Node source) { + exists(FieldRead fr, Field f | fr = source.asExpr() | + f.getAnAccess() = fr and + f.hasQualifiedName("com.azure.storage.blob.specialized.cryptography", "EncryptionVersion", + "V2") + ) + } + + override predicate isSink(DataFlow::Node sink) { + isCreatingAzureClientSideEncryptionObjectNewVersion(_, _, sink.asExpr()) + } +} + +/** + * Holds if `call` is an object creation for a class `EncryptedBlobClientBuilder` + * that takes `versionArg` as the argument specifying the encryption version, and that version is safe. + */ +predicate isCreatingSafeAzureClientSideEncryptionObject(Call call, Class c, Expr versionArg) { + isCreatingAzureClientSideEncryptionObjectNewVersion(call, c, versionArg) and + exists(EncryptedBlobClientBuilderSafeEncryptionVersionConfig config, DataFlow::Node sink | + sink.asExpr() = versionArg + | + config.hasFlow(_, sink) + ) +} + +from Expr e, Class c +where + exists(Expr argVersion | + isCreatingAzureClientSideEncryptionObjectNewVersion(e, c, argVersion) and + not isCreatingSafeAzureClientSideEncryptionObject(e, c, argVersion) + ) + or + isCreatingOutdatedAzureClientSideEncryptionObject(e, c) +select e, "Unsafe usage of v1 version of Azure Storage client-side encryption." diff --git a/java/ql/src/qlpack.yml b/java/ql/src/qlpack.yml index 2366eef3778..8c0538014c1 100644 --- a/java/ql/src/qlpack.yml +++ b/java/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/java-queries -version: 0.2.1-dev +version: 0.3.2-dev groups: - java - queries diff --git a/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll index 5233f92f406..e61d3dabe70 100644 --- a/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll +++ b/java/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -20,3 +20,15 @@ private class KtExpectationComment extends KtComment, ExpectationComment { override string getContents() { result = this.getText().suffix(2).trim() } } + +private class XmlExpectationComment extends ExpectationComment instanceof XMLComment { + override string getContents() { result = this.(XMLComment).getText().trim() } + + override Location getLocation() { result = this.(XMLComment).getLocation() } + + override predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + this.(XMLComment).hasLocationInfo(path, sl, sc, el, ec) + } + + override string toString() { result = this.(XMLComment).toString() } +} diff --git a/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected b/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected index d931ead82ac..b67c634ad9f 100644 --- a/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected +++ b/java/ql/test/experimental/query-tests/security/CWE-200/SensitiveAndroidFileLeak.expected @@ -4,17 +4,13 @@ edges | FileService.java:21:28:21:64 | getStringExtra(...) : Object | FileService.java:25:42:25:50 | localPath : Object | | FileService.java:25:13:25:51 | makeParamsToExecute(...) : Object[] | FileService.java:40:41:40:55 | params : Object[] | | FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | FileService.java:25:13:25:51 | makeParamsToExecute(...) : Object[] | -| FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | FileService.java:40:41:40:55 | params [[]] : Object | | FileService.java:25:42:25:50 | localPath : Object | FileService.java:25:13:25:51 | makeParamsToExecute(...) [[]] : Object | | FileService.java:25:42:25:50 | localPath : Object | FileService.java:32:13:32:28 | sourceUri : Object | | FileService.java:32:13:32:28 | sourceUri : Object | FileService.java:35:17:35:25 | sourceUri : Object | | FileService.java:34:20:36:13 | {...} [[]] : Object | FileService.java:34:20:36:13 | new Object[] [[]] : Object | | FileService.java:35:17:35:25 | sourceUri : Object | FileService.java:34:20:36:13 | {...} [[]] : Object | | FileService.java:40:41:40:55 | params : Object[] | FileService.java:44:33:44:52 | (...)... : Object | -| FileService.java:40:41:40:55 | params [[]] : Object | FileService.java:44:44:44:49 | params [[]] : Object | | FileService.java:44:33:44:52 | (...)... : Object | FileService.java:45:53:45:59 | ...[...] | -| FileService.java:44:44:44:49 | params [[]] : Object | FileService.java:44:44:44:52 | ...[...] : Object | -| FileService.java:44:44:44:52 | ...[...] : Object | FileService.java:44:33:44:52 | (...)... : Object | | LeakFileActivity2.java:15:13:15:18 | intent : Intent | LeakFileActivity2.java:16:26:16:31 | intent : Intent | | LeakFileActivity2.java:16:26:16:31 | intent : Intent | FileService.java:20:31:20:43 | intent : Intent | | LeakFileActivity.java:14:35:14:38 | data : Intent | LeakFileActivity.java:18:40:18:59 | contentIntent : Intent | @@ -34,10 +30,7 @@ nodes | FileService.java:34:20:36:13 | {...} [[]] : Object | semmle.label | {...} [[]] : Object | | FileService.java:35:17:35:25 | sourceUri : Object | semmle.label | sourceUri : Object | | FileService.java:40:41:40:55 | params : Object[] | semmle.label | params : Object[] | -| FileService.java:40:41:40:55 | params [[]] : Object | semmle.label | params [[]] : Object | | FileService.java:44:33:44:52 | (...)... : Object | semmle.label | (...)... : Object | -| FileService.java:44:44:44:49 | params [[]] : Object | semmle.label | params [[]] : Object | -| FileService.java:44:44:44:52 | ...[...] : Object | semmle.label | ...[...] : Object | | FileService.java:45:53:45:59 | ...[...] | semmle.label | ...[...] | | LeakFileActivity2.java:15:13:15:18 | intent : Intent | semmle.label | intent : Intent | | LeakFileActivity2.java:16:26:16:31 | intent : Intent | semmle.label | intent : Intent | diff --git a/java/ql/test/kotlin/library-tests/for-array-iterators/test.expected b/java/ql/test/kotlin/library-tests/for-array-iterators/test.expected new file mode 100644 index 00000000000..ac5821044ff --- /dev/null +++ b/java/ql/test/kotlin/library-tests/for-array-iterators/test.expected @@ -0,0 +1,9 @@ +| test.kt:5:14:5:14 | hasNext(...) | +| test.kt:5:14:5:14 | iterator(...) | +| test.kt:5:14:5:14 | next(...) | +| test.kt:6:14:6:14 | hasNext(...) | +| test.kt:6:14:6:14 | iterator(...) | +| test.kt:6:14:6:14 | next(...) | +| test.kt:7:14:7:14 | hasNext(...) | +| test.kt:7:14:7:14 | iterator(...) | +| test.kt:7:14:7:14 | next(...) | diff --git a/java/ql/test/kotlin/library-tests/for-array-iterators/test.kt b/java/ql/test/kotlin/library-tests/for-array-iterators/test.kt new file mode 100644 index 00000000000..2da3a6e1e0e --- /dev/null +++ b/java/ql/test/kotlin/library-tests/for-array-iterators/test.kt @@ -0,0 +1,11 @@ +fun test(x: Array, y: Array<*>, z: IntArray): Int { + + var ret = 0 + + for (el in x) { ret += 1 } + for (el in y) { ret += 1 } + for (el in z) { ret += 1 } + + return ret + +} diff --git a/java/ql/test/kotlin/library-tests/for-array-iterators/test.ql b/java/ql/test/kotlin/library-tests/for-array-iterators/test.ql new file mode 100644 index 00000000000..ab60ba2525d --- /dev/null +++ b/java/ql/test/kotlin/library-tests/for-array-iterators/test.ql @@ -0,0 +1,4 @@ +import java + +from MethodAccess ma +select ma diff --git a/java/ql/test/kotlin/library-tests/generic-instance-methods/test.expected b/java/ql/test/kotlin/library-tests/generic-instance-methods/test.expected index 13c5c19bd68..b80b1311a64 100644 --- a/java/ql/test/kotlin/library-tests/generic-instance-methods/test.expected +++ b/java/ql/test/kotlin/library-tests/generic-instance-methods/test.expected @@ -14,8 +14,8 @@ calls | test.kt:22:15:22:33 | setter(...) | test.kt:12:1:25:1 | user | test.kt:0:0:0:0 | TestKt | file:///!unknown-binary-location/Generic.class:0:0:0:0 | setter | file:///!unknown-binary-location/Generic.class:0:0:0:0 | Generic | | test.kt:23:15:23:22 | getter(...) | test.kt:12:1:25:1 | user | test.kt:0:0:0:0 | TestKt | file:///!unknown-binary-location/Generic.class:0:0:0:0 | getter | file:///!unknown-binary-location/Generic.class:0:0:0:0 | Generic | constructors -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | Generic2 | Generic2(java.lang.String) | ? extends String | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | Generic2 | Generic2(java.lang.Object) | ? super String | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | Generic2 | Generic2() | | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | Generic2 | Generic2(java.lang.String) | String | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | | Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | Generic2 | Generic2(java.lang.String) | String | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | Generic2(java.lang.Object) | T | void | Test.java:1:7:1:14 | Generic2 | Test.java:3:10:3:17 | Generic2 | | Test.java:14:14:14:17 | Test | Test.java:14:14:14:17 | Test | Test() | No parameters | void | Test.java:14:14:14:17 | Test | Test.java:14:14:14:17 | Test | @@ -34,14 +34,14 @@ refTypes | test.kt:1:1:10:1 | Generic | | test.kt:1:15:1:15 | T | #select -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | getter | getter() | No parameters | ? extends String | Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity | identity(java.lang.String) | ? extends String | ? extends String | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.String) | ? extends String | ? extends String | Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | setter | setter(java.lang.String) | ? extends String | void | Test.java:1:7:1:14 | Generic2 | Test.java:10:8:10:13 | setter | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | getter | getter() | No parameters | ? super String | Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity | identity(java.lang.Object) | ? super String | ? super String | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.Object) | ? super String | ? super String | Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | -| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | setter | setter(java.lang.Object) | ? super String | void | Test.java:1:7:1:14 | Generic2 | Test.java:10:8:10:13 | setter | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | getter | getter() | No parameters | String | Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity | identity() | | String | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity2 | identity2() | | String | Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | setter | setter() | | void | Test.java:1:7:1:14 | Generic2 | Test.java:10:8:10:13 | setter | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | getter | getter() | No parameters | Object | Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity | identity(java.lang.String) | String | Object | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.String) | String | Object | Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | +| Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | setter | setter(java.lang.String) | String | void | Test.java:1:7:1:14 | Generic2 | Test.java:10:8:10:13 | setter | | Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | getter | getter() | No parameters | String | Test.java:1:7:1:14 | Generic2 | Test.java:9:5:9:10 | getter | | Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity | identity(java.lang.String) | String | String | Test.java:1:7:1:14 | Generic2 | Test.java:8:5:8:12 | identity | | Generic2.class:0:0:0:0 | Generic2 | Generic2.class:0:0:0:0 | identity2 | identity2(java.lang.String) | String | String | Test.java:1:7:1:14 | Generic2 | Test.java:7:5:7:13 | identity2 | diff --git a/java/ql/test/kotlin/library-tests/internal-public-alias/User.java b/java/ql/test/kotlin/library-tests/internal-public-alias/User.java new file mode 100644 index 00000000000..d249e1d36f2 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/internal-public-alias/User.java @@ -0,0 +1,11 @@ +public class User { + + public static int test(Test t) { + + t.setInternalVar$main(t.getInternalVal$main()); + + return t.internalFun$main(); + + } + +} diff --git a/java/ql/test/kotlin/library-tests/internal-public-alias/test.expected b/java/ql/test/kotlin/library-tests/internal-public-alias/test.expected new file mode 100644 index 00000000000..77a06cf7310 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/internal-public-alias/test.expected @@ -0,0 +1,6 @@ +| User.java:3:21:3:24 | test | +| test.kt:3:12:3:30 | getInternalVal$main | +| test.kt:6:3:6:36 | getInternalVal | +| test.kt:8:12:8:30 | getInternalVar$main | +| test.kt:8:12:8:30 | setInternalVar$main | +| test.kt:10:12:10:32 | internalFun$main | diff --git a/java/ql/test/kotlin/library-tests/internal-public-alias/test.kt b/java/ql/test/kotlin/library-tests/internal-public-alias/test.kt new file mode 100644 index 00000000000..e79e6d2f907 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/internal-public-alias/test.kt @@ -0,0 +1,12 @@ +public class Test { + + internal val internalVal = 1 + + // Would clash with the internal val's getter without name mangling and provoke a database inconsistency: + fun getInternalVal() = internalVal + + internal var internalVar = 2 + + internal fun internalFun() = 3 + +} diff --git a/java/ql/test/kotlin/library-tests/internal-public-alias/test.ql b/java/ql/test/kotlin/library-tests/internal-public-alias/test.ql new file mode 100644 index 00000000000..f1355df2e88 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/internal-public-alias/test.ql @@ -0,0 +1,5 @@ +import java + +from Method m +where m.fromSource() +select m diff --git a/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected index 417b8a22399..7995948aa78 100644 --- a/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected +++ b/java/ql/test/kotlin/library-tests/java-kotlin-collection-type-generic-methods/test.expected @@ -54,7 +54,7 @@ methodWithDuplicate | AbstractList | set | int | | AbstractList | subList | int | | AbstractList | subListRangeCheck | int | -| AbstractMap | containsEntry | Entry | +| AbstractMap | containsEntry$kotlin_stdlib | Entry | | AbstractMap | containsKey | Object | | AbstractMap | containsValue | Object | | AbstractMap | equals | Object | @@ -79,7 +79,7 @@ methodWithDuplicate | AbstractMap | put | V | | AbstractMap | putAll | Map | | AbstractMap | remove | Object | -| AbstractMap | containsEntry | Entry | +| AbstractMap | containsEntry$kotlin_stdlib | Entry | | AbstractMap | containsKey | Object | | AbstractMap | containsValue | Object | | AbstractMap | equals | Object | diff --git a/java/ql/test/kotlin/library-tests/methods/methods.expected b/java/ql/test/kotlin/library-tests/methods/methods.expected index 11fa8762e28..8067838d889 100644 --- a/java/ql/test/kotlin/library-tests/methods/methods.expected +++ b/java/ql/test/kotlin/library-tests/methods/methods.expected @@ -42,7 +42,7 @@ methods | methods.kt:5:1:20:1 | Class | methods.kt:14:12:14:29 | publicFun | publicFun() | public | | | methods.kt:5:1:20:1 | Class | methods.kt:15:15:15:35 | protectedFun | protectedFun() | protected | | | methods.kt:5:1:20:1 | Class | methods.kt:16:13:16:31 | privateFun | privateFun() | private | | -| methods.kt:5:1:20:1 | Class | methods.kt:17:14:17:33 | internalFun | internalFun() | internal | | +| methods.kt:5:1:20:1 | Class | methods.kt:17:14:17:33 | internalFun$main | internalFun$main() | internal | | | methods.kt:5:1:20:1 | Class | methods.kt:18:5:18:36 | noExplicitVisibilityFun | noExplicitVisibilityFun() | public | | | methods.kt:5:1:20:1 | Class | methods.kt:19:12:19:29 | inlineFun | inlineFun() | inline, public | | constructors diff --git a/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected b/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected index 70345c576b0..14b5124b7ae 100644 --- a/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected +++ b/java/ql/test/kotlin/library-tests/modifiers/modifiers.expected @@ -11,7 +11,7 @@ | modifiers.kt:4:5:4:22 | c | Field | final | | modifiers.kt:4:5:4:22 | c | Field | private | | modifiers.kt:4:5:4:22 | c | Property | internal | -| modifiers.kt:4:14:4:22 | getC | Method | internal | +| modifiers.kt:4:14:4:22 | getC$main | Method | internal | | modifiers.kt:5:5:5:34 | d | Field | final | | modifiers.kt:5:5:5:34 | d | Field | private | | modifiers.kt:5:5:5:34 | d | Property | public | diff --git a/java/ql/test/kotlin/library-tests/properties/properties.expected b/java/ql/test/kotlin/library-tests/properties/properties.expected index 05870c7b6e1..7bbe706923c 100644 --- a/java/ql/test/kotlin/library-tests/properties/properties.expected +++ b/java/ql/test/kotlin/library-tests/properties/properties.expected @@ -45,7 +45,7 @@ fieldDeclarations | properties.kt:35:5:35:32 | privateProp | properties.kt:35:13:35:32 | getPrivateProp$private | file://:0:0:0:0 | | properties.kt:35:5:35:32 | privateProp | private | | properties.kt:36:5:36:36 | protectedProp | properties.kt:36:15:36:36 | getProtectedProp | file://:0:0:0:0 | | properties.kt:36:5:36:36 | protectedProp | protected | | properties.kt:37:5:37:30 | publicProp | properties.kt:37:12:37:30 | getPublicProp | file://:0:0:0:0 | | properties.kt:37:5:37:30 | publicProp | public | -| properties.kt:38:5:38:34 | internalProp | properties.kt:38:14:38:34 | getInternalProp | file://:0:0:0:0 | | properties.kt:38:5:38:34 | internalProp | internal | +| properties.kt:38:5:38:34 | internalProp | properties.kt:38:14:38:34 | getInternalProp$main | file://:0:0:0:0 | | properties.kt:38:5:38:34 | internalProp | internal | | properties.kt:67:1:67:23 | constVal | properties.kt:67:7:67:23 | getConstVal | file://:0:0:0:0 | | properties.kt:67:1:67:23 | constVal | public | | properties.kt:70:5:70:16 | prop | properties.kt:70:5:70:16 | getProp | file://:0:0:0:0 | | properties.kt:70:5:70:16 | prop | public | | properties.kt:78:1:79:13 | x | properties.kt:79:5:79:13 | getX | file://:0:0:0:0 | | file://:0:0:0:0 | | public | diff --git a/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected b/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected index dd47be234c3..b1da7cdcee2 100644 --- a/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected +++ b/java/ql/test/kotlin/library-tests/reflection/PrintAst.expected @@ -8,7 +8,7 @@ reflection.kt: # 46| 0: [TypeAccess] String # 47| 5: [BlockStmt] { ... } # 47| 0: [ReturnStmt] return ... -# 47| 0: [MethodAccess] get(...) +# 47| 0: [MethodAccess] charAt(...) # 47| -1: [ExtensionReceiverAccess] this # 47| 0: [SubExpr] ... - ... # 47| 0: [MethodAccess] length(...) diff --git a/java/ql/test/kotlin/library-tests/string-charat/Test.java b/java/ql/test/kotlin/library-tests/string-charat/Test.java new file mode 100644 index 00000000000..22f553216b7 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/string-charat/Test.java @@ -0,0 +1,5 @@ +public class Test { + + public char f(String s) { return s.charAt(0); } + +} diff --git a/java/ql/test/kotlin/library-tests/string-charat/test.expected b/java/ql/test/kotlin/library-tests/string-charat/test.expected new file mode 100644 index 00000000000..ab1ff9b6d5f --- /dev/null +++ b/java/ql/test/kotlin/library-tests/string-charat/test.expected @@ -0,0 +1,2 @@ +| Test.java:3:36:3:46 | charAt(...) | +| test.kt:2:20:2:23 | charAt(...) | diff --git a/java/ql/test/kotlin/library-tests/string-charat/test.kt b/java/ql/test/kotlin/library-tests/string-charat/test.kt new file mode 100644 index 00000000000..c586f3d0171 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/string-charat/test.kt @@ -0,0 +1,2 @@ + +fun f(x: String) = x[0] diff --git a/java/ql/test/kotlin/library-tests/string-charat/test.ql b/java/ql/test/kotlin/library-tests/string-charat/test.ql new file mode 100644 index 00000000000..ab60ba2525d --- /dev/null +++ b/java/ql/test/kotlin/library-tests/string-charat/test.ql @@ -0,0 +1,4 @@ +import java + +from MethodAccess ma +select ma diff --git a/java/ql/test/kotlin/library-tests/super-method-calls/test.expected b/java/ql/test/kotlin/library-tests/super-method-calls/test.expected new file mode 100644 index 00000000000..841eb6298c7 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/super-method-calls/test.expected @@ -0,0 +1,2 @@ +| test.kt:31:17:31:24 | source(...) | test.kt:31:15:31:25 | f(...) | +| test.kt:32:17:32:24 | source(...) | test.kt:32:15:32:25 | g(...) | diff --git a/java/ql/test/kotlin/library-tests/super-method-calls/test.kt b/java/ql/test/kotlin/library-tests/super-method-calls/test.kt new file mode 100644 index 00000000000..7b3e90d7359 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/super-method-calls/test.kt @@ -0,0 +1,36 @@ +open class A { + + open fun f(x: String) = x + +} + +interface B { + + fun g(x: String) = x + +} + +interface C { + + fun g(x: String) = x + +} + +class User : A(), B, C { + + override fun f(x: String) = super.f(x) + + override fun g(x: String) = super.g(x) + + fun source() = "tainted" + + fun sink(s: String) { } + + fun test() { + + sink(this.f(source())) + sink(this.g(source())) + + } + +} diff --git a/java/ql/test/kotlin/library-tests/super-method-calls/test.ql b/java/ql/test/kotlin/library-tests/super-method-calls/test.ql new file mode 100644 index 00000000000..9a628624c91 --- /dev/null +++ b/java/ql/test/kotlin/library-tests/super-method-calls/test.ql @@ -0,0 +1,18 @@ +import java +import semmle.code.java.dataflow.DataFlow + +class Config extends DataFlow::Configuration { + Config() { this = "abc" } + + override predicate isSource(DataFlow::Node n) { + n.asExpr().(MethodAccess).getMethod().getName() = "source" + } + + override predicate isSink(DataFlow::Node n) { + n.asExpr().(Argument).getCall().getCallee().getName() = "sink" + } +} + +from Config c, DataFlow::Node n1, DataFlow::Node n2 +where c.hasFlow(n1, n2) +select n1, n2 diff --git a/java/ql/test/library-tests/dataflow/collections/Test.java b/java/ql/test/library-tests/dataflow/collections/Test.java index 216f373ca8c..9836070f42c 100644 --- a/java/ql/test/library-tests/dataflow/collections/Test.java +++ b/java/ql/test/library-tests/dataflow/collections/Test.java @@ -78,4 +78,14 @@ public class Test { sink(x18); // Flow }); } + + public void run4() { + Properties p = new Properties(); + p.put("key", tainted); + sink(p.getProperty("key")); // Flow + sink(p.getProperty("key", "defaultValue")); // Flow + + Properties clean = new Properties(); + sink(clean.getProperty("key", tainted)); // Flow + } } diff --git a/java/ql/test/library-tests/dataflow/collections/flow.expected b/java/ql/test/library-tests/dataflow/collections/flow.expected index 1a4bed0a6e7..875a4191722 100644 --- a/java/ql/test/library-tests/dataflow/collections/flow.expected +++ b/java/ql/test/library-tests/dataflow/collections/flow.expected @@ -11,3 +11,6 @@ | Test.java:49:20:49:26 | tainted | Test.java:60:12:60:14 | x14 | | Test.java:73:11:73:17 | tainted | Test.java:75:10:75:12 | x17 | | Test.java:73:11:73:17 | tainted | Test.java:78:12:78:14 | x18 | +| Test.java:84:18:84:24 | tainted | Test.java:85:10:85:29 | getProperty(...) | +| Test.java:84:18:84:24 | tainted | Test.java:86:10:86:45 | getProperty(...) | +| Test.java:89:35:89:41 | tainted | Test.java:89:10:89:42 | getProperty(...) | diff --git a/java/ql/test/library-tests/frameworks/android/asynctask/Test.java b/java/ql/test/library-tests/frameworks/android/asynctask/Test.java index a2c25487462..6f0bc1e84fa 100644 --- a/java/ql/test/library-tests/frameworks/android/asynctask/Test.java +++ b/java/ql/test/library-tests/frameworks/android/asynctask/Test.java @@ -10,16 +10,19 @@ public class Test { public void test() { TestAsyncTask t = new TestAsyncTask(); - t.execute(source("execute")); - t.executeOnExecutor(null, source("executeOnExecutor")); + t.execute(source("execute"), null); + t.executeOnExecutor(null, source("executeOnExecutor"), null); SafeAsyncTask t2 = new SafeAsyncTask(); t2.execute("safe"); + TestConstructorTask t3 = new TestConstructorTask(source("constructor"), "safe"); + t3.execute(source("params")); } private class TestAsyncTask extends AsyncTask { @Override protected Object doInBackground(Object... params) { - sink(params); // $ hasValueFlow=execute hasValueFlow=executeOnExecutor + sink(params[0]); // $ hasTaintFlow=execute hasTaintFlow=executeOnExecutor + sink(params[1]); // $ SPURIOUS: hasTaintFlow=execute hasTaintFlow=executeOnExecutor return null; } } @@ -27,8 +30,40 @@ public class Test { private class SafeAsyncTask extends AsyncTask { @Override protected Object doInBackground(Object... params) { - sink(params); // Safe + sink(params[0]); // Safe return null; } } + + static class TestConstructorTask extends AsyncTask { + private Object field; + private Object safeField; + private Object initField; + { + initField = Test.source("init"); + } + + public TestConstructorTask(Object field, Object safeField) { + this.field = field; + this.safeField = safeField; + } + + @Override + protected Object doInBackground(Object... params) { + sink(params[0]); // $ hasTaintFlow=params + sink(field); // $ hasValueFlow=constructor + sink(safeField); // Safe + sink(initField); // $ hasValueFlow=init + return params[0]; + } + + @Override + protected void onPostExecute(Object param) { + sink(param); // $ hasTaintFlow=params + sink(field); // $ hasValueFlow=constructor + sink(safeField); // Safe + sink(initField); // $ hasValueFlow=init + } + + } } diff --git a/java/ql/test/library-tests/frameworks/android/asynctask/test.ql b/java/ql/test/library-tests/frameworks/android/asynctask/test.ql index 9c9b3747604..5d91e4e8e26 100644 --- a/java/ql/test/library-tests/frameworks/android/asynctask/test.ql +++ b/java/ql/test/library-tests/frameworks/android/asynctask/test.ql @@ -1,6 +1,2 @@ import java import TestUtilities.InlineFlowTest - -class AsyncTaskTest extends InlineFlowTest { - override TaintTracking::Configuration getTaintFlowConfig() { none() } -} diff --git a/java/ql/test/library-tests/scanner/Test.java b/java/ql/test/library-tests/scanner/Test.java new file mode 100644 index 00000000000..05f5b497215 --- /dev/null +++ b/java/ql/test/library-tests/scanner/Test.java @@ -0,0 +1,328 @@ +package generatedtest; + +import java.io.File; +import java.io.InputStream; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.nio.channels.ReadableByteChannel; +import java.nio.charset.Charset; +import java.nio.file.Path; +import java.util.Scanner; +import java.util.regex.Pattern; + +// Test case generated by GenerateFlowTestCase.ql +public class Test { + + Object source() { + return null; + } + + void sink(Object o) {} + + public void test() throws Exception { + + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + File in = (File) source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + File in = (File) source(); + out = new Scanner(in, (Charset) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + File in = (File) source(); + out = new Scanner(in, (String) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + InputStream in = (InputStream) source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + InputStream in = (InputStream) source(); + out = new Scanner(in, (Charset) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + InputStream in = (InputStream) source(); + out = new Scanner(in, (String) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + Path in = (Path) source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + Path in = (Path) source(); + out = new Scanner(in, (Charset) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + Path in = (Path) source(); + out = new Scanner(in, (String) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + Readable in = (Readable) source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + ReadableByteChannel in = (ReadableByteChannel) source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + ReadableByteChannel in = (ReadableByteChannel) source(); + out = new Scanner(in, (Charset) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + ReadableByteChannel in = (ReadableByteChannel) source(); + out = new Scanner(in, (String) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;Scanner;;;Argument[0];Argument[-1];taint;manual" + Scanner out = null; + String in = (String) source(); + out = new Scanner(in); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;findInLine;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.findInLine((Pattern) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;findInLine;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.findInLine((String) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.findWithinHorizon((Pattern) null, 0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;findWithinHorizon;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.findWithinHorizon((String) null, 0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.next((Pattern) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.next((String) null); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;next;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.next(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextBigDecimal;;;Argument[-1];ReturnValue;taint;manual" + BigDecimal out = null; + Scanner in = (Scanner) source(); + out = in.nextBigDecimal(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextBigInteger;;;Argument[-1];ReturnValue;taint;manual" + BigInteger out = null; + Scanner in = (Scanner) source(); + out = in.nextBigInteger(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextBigInteger;;;Argument[-1];ReturnValue;taint;manual" + BigInteger out = null; + Scanner in = (Scanner) source(); + out = in.nextBigInteger(0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextBoolean;;;Argument[-1];ReturnValue;taint;manual" + boolean out = false; + Scanner in = (Scanner) source(); + out = in.nextBoolean(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextByte;;;Argument[-1];ReturnValue;taint;manual" + byte out = 0; + Scanner in = (Scanner) source(); + out = in.nextByte(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextByte;;;Argument[-1];ReturnValue;taint;manual" + byte out = 0; + Scanner in = (Scanner) source(); + out = in.nextByte(0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextDouble;;;Argument[-1];ReturnValue;taint;manual" + double out = 0; + Scanner in = (Scanner) source(); + out = in.nextDouble(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextFloat;;;Argument[-1];ReturnValue;taint;manual" + float out = 0; + Scanner in = (Scanner) source(); + out = in.nextFloat(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextInt;;;Argument[-1];ReturnValue;taint;manual" + int out = 0; + Scanner in = (Scanner) source(); + out = in.nextInt(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextInt;;;Argument[-1];ReturnValue;taint;manual" + int out = 0; + Scanner in = (Scanner) source(); + out = in.nextInt(0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextLine;;;Argument[-1];ReturnValue;taint;manual" + String out = null; + Scanner in = (Scanner) source(); + out = in.nextLine(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextLong;;;Argument[-1];ReturnValue;taint;manual" + long out = 0; + Scanner in = (Scanner) source(); + out = in.nextLong(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextLong;;;Argument[-1];ReturnValue;taint;manual" + long out = 0; + Scanner in = (Scanner) source(); + out = in.nextLong(0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextShort;;;Argument[-1];ReturnValue;taint;manual" + short out = 0; + Scanner in = (Scanner) source(); + out = in.nextShort(); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;nextShort;;;Argument[-1];ReturnValue;taint;manual" + short out = 0; + Scanner in = (Scanner) source(); + out = in.nextShort(0); + sink(out); // $ hasTaintFlow + } + { + // "java.util;Scanner;true;reset;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner) source(); + out = in.reset(); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner) source(); + out = in.skip((Pattern) null); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;skip;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner) source(); + out = in.skip((String) null); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner) source(); + out = in.useDelimiter((Pattern) null); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;useDelimiter;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner) source(); + out = in.useDelimiter((String) null); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;useLocale;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner) source(); + out = in.useLocale(null); + sink(out); // $ hasValueFlow + } + { + // "java.util;Scanner;true;useRadix;;;Argument[-1];ReturnValue;value;manual" + Scanner out = null; + Scanner in = (Scanner) source(); + out = in.useRadix(0); + sink(out); // $ hasValueFlow + } + + } + +} diff --git a/swift/integration-tests/frontend-invocations/A.swift b/java/ql/test/library-tests/scanner/test.expected similarity index 100% rename from swift/integration-tests/frontend-invocations/A.swift rename to java/ql/test/library-tests/scanner/test.expected diff --git a/java/ql/test/library-tests/scanner/test.ql b/java/ql/test/library-tests/scanner/test.ql new file mode 100644 index 00000000000..5d91e4e8e26 --- /dev/null +++ b/java/ql/test/library-tests/scanner/test.ql @@ -0,0 +1,2 @@ +import java +import TestUtilities.InlineFlowTest diff --git a/java/ql/test/library-tests/types/cycles/Test.java b/java/ql/test/library-tests/types/cycles/Test.java new file mode 100644 index 00000000000..a06540728b9 --- /dev/null +++ b/java/ql/test/library-tests/types/cycles/Test.java @@ -0,0 +1,2 @@ +public class Test { +} diff --git a/java/ql/test/library-tests/types/cycles/cycles.expected b/java/ql/test/library-tests/types/cycles/cycles.expected new file mode 100644 index 00000000000..7c0e79d7252 --- /dev/null +++ b/java/ql/test/library-tests/types/cycles/cycles.expected @@ -0,0 +1,6 @@ +| BiFunction | +| BiFunction | +| Function | +| Function | +| Map | +| Map | diff --git a/java/ql/test/library-tests/types/cycles/cycles.ql b/java/ql/test/library-tests/types/cycles/cycles.ql new file mode 100644 index 00000000000..dd4d6e1f757 --- /dev/null +++ b/java/ql/test/library-tests/types/cycles/cycles.ql @@ -0,0 +1,5 @@ +import java + +from RefType t +where t = t.getAStrictAncestor() +select t.toString() diff --git a/java/ql/test/library-tests/wildcard-substitution/Lib.java b/java/ql/test/library-tests/wildcard-substitution/Lib.java new file mode 100644 index 00000000000..0647410b692 --- /dev/null +++ b/java/ql/test/library-tests/wildcard-substitution/Lib.java @@ -0,0 +1,20 @@ +import java.util.List; + +public class Lib { + + public void takesVar(T t) { } + public void takesInvar(List lt) { } + public void takesUnbound(List lt) { } + public void takesExtends(List lt) { } + public void takesSuper(List lt) { } + + public T returnsVar() { return null; } + public List returnsInvar() { return null; } + public List returnsUnbound() { return null; } + public List returnsExtends() { return null; } + public List returnsSuper() { return null; } + + public void takesArray(T[] ts) { } + public T[] returnsArray() { return null; } + +} diff --git a/java/ql/test/library-tests/wildcard-substitution/User.java b/java/ql/test/library-tests/wildcard-substitution/User.java new file mode 100644 index 00000000000..5f4b7fac21a --- /dev/null +++ b/java/ql/test/library-tests/wildcard-substitution/User.java @@ -0,0 +1,12 @@ +public class User { + + public static void test(Lib invarLib, Lib extendsLib, Lib superLib, Lib unboundLib) { + + invarLib.takesVar(null); + extendsLib.takesVar(null); + superLib.takesVar(null); + unboundLib.takesVar(null); + + } + +} diff --git a/java/ql/test/library-tests/wildcard-substitution/test.expected b/java/ql/test/library-tests/wildcard-substitution/test.expected new file mode 100644 index 00000000000..91a1cc43cb0 --- /dev/null +++ b/java/ql/test/library-tests/wildcard-substitution/test.expected @@ -0,0 +1,64 @@ +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsArray | CharSequence[] | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | CharSequence | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesArray | [] | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsArray | Object[] | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | Object | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesArray | CharSequence[] | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | CharSequence | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsArray | Object[] | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | Object | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesArray | [] | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsArray | CharSequence[] | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | returnsVar | CharSequence | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesArray | CharSequence[] | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesExtends | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesInvar | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesSuper | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesUnbound | List | +| Lib.class:0:0:0:0 | Lib | Lib.class:0:0:0:0 | takesVar | CharSequence | +| Lib.java:3:14:3:16 | Lib | Lib.java:5:15:5:22 | takesVar | T | +| Lib.java:3:14:3:16 | Lib | Lib.java:6:15:6:24 | takesInvar | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:7:15:7:26 | takesUnbound | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:8:15:8:26 | takesExtends | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:9:15:9:24 | takesSuper | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:11:12:11:21 | returnsVar | T | +| Lib.java:3:14:3:16 | Lib | Lib.java:12:18:12:29 | returnsInvar | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:13:18:13:31 | returnsUnbound | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:14:28:14:41 | returnsExtends | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:15:26:15:37 | returnsSuper | List | +| Lib.java:3:14:3:16 | Lib | Lib.java:17:15:17:24 | takesArray | T[] | +| Lib.java:3:14:3:16 | Lib | Lib.java:18:14:18:25 | returnsArray | T[] | +| User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | +| User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | +| User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | +| User.java:1:14:1:17 | User | User.java:3:22:3:25 | test | Lib | diff --git a/java/ql/test/library-tests/wildcard-substitution/test.ql b/java/ql/test/library-tests/wildcard-substitution/test.ql new file mode 100644 index 00000000000..3fdf1c00a55 --- /dev/null +++ b/java/ql/test/library-tests/wildcard-substitution/test.ql @@ -0,0 +1,7 @@ +import java + +Type notVoid(Type t) { result = t and not result instanceof VoidType } + +from Callable c +where c.getSourceDeclaration().fromSource() +select c.getDeclaringType(), c, notVoid([c.getAParamType(), c.getReturnType()]).toString() diff --git a/java/ql/test/library-tests/xml/Test.java b/java/ql/test/library-tests/xml/Test.java new file mode 100644 index 00000000000..4566fbca2ad --- /dev/null +++ b/java/ql/test/library-tests/xml/Test.java @@ -0,0 +1,3 @@ +public class Test { + +} diff --git a/java/ql/test/library-tests/xml/XMLTest.expected b/java/ql/test/library-tests/xml/XMLTest.expected new file mode 100644 index 00000000000..61d3c801963 --- /dev/null +++ b/java/ql/test/library-tests/xml/XMLTest.expected @@ -0,0 +1,2 @@ +| test.xml:4:5:4:32 | attribute=value | Unexpected result: hasXmlResult= | +| test.xml:5:29:5:52 | $ hasXmlResult | Missing result:hasXmlResult= | \ No newline at end of file diff --git a/java/ql/test/library-tests/xml/XMLTest.ql b/java/ql/test/library-tests/xml/XMLTest.ql new file mode 100644 index 00000000000..fb51e0a1506 --- /dev/null +++ b/java/ql/test/library-tests/xml/XMLTest.ql @@ -0,0 +1,17 @@ +import semmle.code.xml.XML +import TestUtilities.InlineExpectationsTest + +class XmlTest extends InlineExpectationsTest { + XmlTest() { this = "XmlTest" } + + override string getARelevantTag() { result = "hasXmlResult" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasXmlResult" and + exists(XMLAttribute a | + a.getLocation() = location and + element = a.toString() and + value = "" + ) + } +} diff --git a/java/ql/test/library-tests/xml/test.xml b/java/ql/test/library-tests/xml/test.xml new file mode 100644 index 00000000000..f0d9262f09f --- /dev/null +++ b/java/ql/test/library-tests/xml/test.xml @@ -0,0 +1,6 @@ + + + Text + Text + Text + \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected index 13ac840300d..830f4d76085 100644 --- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected +++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected @@ -8,6 +8,7 @@ edges | Test.java:79:74:79:97 | getInputStream(...) : ServletInputStream | Test.java:79:52:79:98 | new InputStreamReader(...) : InputStreamReader | | Test.java:80:31:80:32 | br : BufferedReader | Test.java:80:31:80:43 | readLine(...) : String | | Test.java:80:31:80:43 | readLine(...) : String | Test.java:82:67:82:81 | ... + ... | +| Test.java:88:17:88:37 | getHostName(...) : String | Test.java:90:26:90:29 | temp | nodes | Test.java:19:18:19:38 | getHostName(...) : String | semmle.label | getHostName(...) : String | | Test.java:24:20:24:23 | temp | semmle.label | temp | @@ -20,6 +21,8 @@ nodes | Test.java:80:31:80:32 | br : BufferedReader | semmle.label | br : BufferedReader | | Test.java:80:31:80:43 | readLine(...) : String | semmle.label | readLine(...) : String | | Test.java:82:67:82:81 | ... + ... | semmle.label | ... + ... | +| Test.java:88:17:88:37 | getHostName(...) : String | semmle.label | getHostName(...) : String | +| Test.java:90:26:90:29 | temp | semmle.label | temp | subpaths #select | Test.java:24:11:24:24 | new File(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:24:20:24:23 | temp | $@ flows to here and is used in a path. | Test.java:19:18:19:38 | getHostName(...) | User-provided value | @@ -27,3 +30,4 @@ subpaths | Test.java:30:11:30:48 | getPath(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:30:44:30:47 | temp | $@ flows to here and is used in a path. | Test.java:19:18:19:38 | getHostName(...) | User-provided value | | Test.java:34:12:34:25 | new File(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:34:21:34:24 | temp | $@ flows to here and is used in a path. | Test.java:19:18:19:38 | getHostName(...) | User-provided value | | Test.java:82:52:82:88 | new FileWriter(...) | Test.java:79:74:79:97 | getInputStream(...) : ServletInputStream | Test.java:82:67:82:81 | ... + ... | $@ flows to here and is used in a path. | Test.java:79:74:79:97 | getInputStream(...) | User-provided value | +| Test.java:90:26:90:29 | temp | Test.java:88:17:88:37 | getHostName(...) : String | Test.java:90:26:90:29 | temp | $@ flows to here and is used in a path. | Test.java:88:17:88:37 | getHostName(...) | User-provided value | diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java b/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java index a0a6694c061..f0d0147df08 100644 --- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java +++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java @@ -2,7 +2,6 @@ // http://cwe.mitre.org/data/definitions/22.html package test.cwe22.semmle.tests; - import javax.servlet.http.*; import javax.servlet.ServletException; @@ -12,6 +11,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.FileSystems; +import org.apache.commons.io.output.LockableFileWriter; class Test { void doGet1(InetAddress address) @@ -19,13 +19,13 @@ class Test { String temp = address.getHostName(); File file; Path path; - + // BAD: construct a file path with user input file = new File(temp); - + // BAD: construct a path with user input path = Paths.get(temp); - + // BAD: construct a path with user input path = FileSystems.getDefault().getPath(temp); @@ -34,7 +34,7 @@ class Test { file = new File(temp); } } - + void doGet2(InetAddress address) throws IOException { String temp = address.getHostName(); @@ -44,7 +44,7 @@ class Test { if(isSafe(temp)) file = new File(temp); } - + void doGet3(InetAddress address) throws IOException { String temp = address.getHostName(); @@ -66,7 +66,7 @@ class Test { return false; return true; } - + boolean isSortOfSafe(String pathSpec) { // no file separators if (pathSpec.contains(File.separator)) @@ -82,4 +82,11 @@ class Test { BufferedWriter bw = new BufferedWriter(new FileWriter("dir/"+filename, true)); } } + + void doGet4(InetAddress address) + throws IOException { + String temp = address.getHostName(); + // BAD: open a file based on user input, using a MaD-documented API + new LockableFileWriter(temp); + } } diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/options b/java/ql/test/query-tests/security/CWE-022/semmle/tests/options index a41b28dc245..6f216f46554 100644 --- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/options +++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/options @@ -1 +1 @@ -// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4 +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/servlet-api-2.4:${testdir}/../../../../../stubs/apache-commons-io-2.6 diff --git a/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java new file mode 100644 index 00000000000..180989590f0 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/Test.java @@ -0,0 +1,54 @@ +import android.webkit.WebViewClient; +import android.webkit.WebView; +import android.webkit.SslErrorHandler; +import android.net.http.SslError; +import android.net.http.SslCertificate; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.app.Activity; + +class Test { + class A extends WebViewClient { + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { // $hasResult + handler.proceed(); + } + } + + interface Validator { + boolean isValid(SslCertificate cert); + } + + class B extends WebViewClient { + Validator v; + + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { + if (this.v.isValid(error.getCertificate())) { + handler.proceed(); + } + else { + handler.cancel(); + } + } + } + + class C extends WebViewClient { + Activity activity; + + public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) { + new AlertDialog.Builder(activity). + setTitle("SSL error"). + setMessage("SSL error. Connect anyway?"). + setPositiveButton("Yes", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + handler.proceed(); + } + }).setNegativeButton("No", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + handler.cancel(); + } + }).show(); + } + } +} \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/options b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/options new file mode 100644 index 00000000000..a36a7e0c5ee --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/options @@ -0,0 +1 @@ +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/google-android-9.0.0 \ No newline at end of file diff --git a/swift/integration-tests/frontend-invocations/B.swift b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.expected similarity index 100% rename from swift/integration-tests/frontend-invocations/B.swift rename to java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.expected diff --git a/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.ql b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.ql new file mode 100644 index 00000000000..6166e0fe239 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-295/ImproperWebVeiwCertificateValidation/test.ql @@ -0,0 +1,19 @@ +import java +import semmle.code.java.security.AndroidWebViewCertificateValidationQuery +import TestUtilities.InlineExpectationsTest + +class WebViewTest extends InlineExpectationsTest { + WebViewTest() { this = "WebViewTest" } + + override string getARelevantTag() { result = "hasResult" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + exists(OnReceivedSslErrorMethod m | + trustsAllCerts(m) and + location = m.getLocation() and + element = m.toString() and + tag = "hasResult" and + value = "" + ) + } +} diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTestKt.kt b/java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTestKt.kt new file mode 100644 index 00000000000..f2f18eaeb0a --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTestKt.kt @@ -0,0 +1,11 @@ +import android.app.Activity +import android.content.Context +import android.content.SharedPreferences + +class CleartextStorageSharedPrefsTestKt : Activity() { + fun testSetSharedPrefs1(context: Context, name: String, password: String) { + val sharedPrefs = context.getSharedPreferences("user_prefs", Context.MODE_PRIVATE); + sharedPrefs.edit().putString("name", name).apply(); // Safe + sharedPrefs.edit().putString("password", password).apply(); // $ hasCleartextStorageSharedPrefs + } +} diff --git a/java/ql/test/query-tests/security/CWE-312/options b/java/ql/test/query-tests/security/CWE-312/options index f017f81ff2f..93845c4b2a8 100644 --- a/java/ql/test/query-tests/security/CWE-312/options +++ b/java/ql/test/query-tests/security/CWE-312/options @@ -1 +1,2 @@ // semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0 +// codeql-extractor-kotlin-options: ${testdir}/../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/query-tests/security/CWE-749/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-749/AndroidManifest.xml index b215e4d3466..79d79d0cd10 100755 --- a/java/ql/test/query-tests/security/CWE-749/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-749/AndroidManifest.xml @@ -44,6 +44,7 @@ + diff --git a/java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt b/java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt new file mode 100644 index 00000000000..d20845f5c77 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-749/UnsafeActivityKt.kt @@ -0,0 +1,20 @@ +package com.example.app + +import android.app.Activity +import android.os.Bundle +import android.webkit.WebSettings +import android.webkit.WebView +import android.webkit.WebViewClient + +class UnsafeActivityKt : Activity() { + override fun onCreate(savedInstanceState : Bundle) { + + val wv = findViewById(-1) + // Implicit not-nulls happening here + wv.settings.setJavaScriptEnabled(true) + wv.settings.setAllowFileAccessFromFileURLs(true) + + val thisUrl : String = intent.extras.getString("url") + wv.loadUrl(thisUrl) // $ hasUnsafeAndroidAccess + } +} diff --git a/java/ql/test/query-tests/security/CWE-749/options b/java/ql/test/query-tests/security/CWE-749/options index d6a9adcece3..49f527db1db 100644 --- a/java/ql/test/query-tests/security/CWE-749/options +++ b/java/ql/test/query-tests/security/CWE-749/options @@ -1 +1,2 @@ -//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/android +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0 +//codeql-extractor-kotlin-options: ${testdir}/../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/stubs/apache-commons-io-2.6/org/apache/commons/io/output/LockableFileWriter.java b/java/ql/test/stubs/apache-commons-io-2.6/org/apache/commons/io/output/LockableFileWriter.java new file mode 100644 index 00000000000..3c7c9c5ebf5 --- /dev/null +++ b/java/ql/test/stubs/apache-commons-io-2.6/org/apache/commons/io/output/LockableFileWriter.java @@ -0,0 +1,7 @@ +package org.apache.commons.io.output; + +public class LockableFileWriter { + + public LockableFileWriter(String filename) { } + +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/ActionBar.java b/java/ql/test/stubs/google-android-9.0.0/android/app/ActionBar.java new file mode 100644 index 00000000000..ae42ac532dc --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/ActionBar.java @@ -0,0 +1,132 @@ +// Generated automatically from android.app.ActionBar for testing purposes + +package android.app; + +import android.app.FragmentTransaction; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.SpinnerAdapter; + +abstract public class ActionBar +{ + abstract static public class Tab + { + public Tab(){} + public abstract ActionBar.Tab setContentDescription(CharSequence p0); + public abstract ActionBar.Tab setContentDescription(int p0); + public abstract ActionBar.Tab setCustomView(View p0); + public abstract ActionBar.Tab setCustomView(int p0); + public abstract ActionBar.Tab setIcon(Drawable p0); + public abstract ActionBar.Tab setIcon(int p0); + public abstract ActionBar.Tab setTabListener(ActionBar.TabListener p0); + public abstract ActionBar.Tab setTag(Object p0); + public abstract ActionBar.Tab setText(CharSequence p0); + public abstract ActionBar.Tab setText(int p0); + public abstract CharSequence getContentDescription(); + public abstract CharSequence getText(); + public abstract Drawable getIcon(); + public abstract Object getTag(); + public abstract View getCustomView(); + public abstract int getPosition(); + public abstract void select(); + public static int INVALID_POSITION = 0; + } + public ActionBar(){} + public Context getThemedContext(){ return null; } + public abstract ActionBar.Tab getSelectedTab(); + public abstract ActionBar.Tab getTabAt(int p0); + public abstract ActionBar.Tab newTab(); + public abstract CharSequence getSubtitle(); + public abstract CharSequence getTitle(); + public abstract View getCustomView(); + public abstract boolean isShowing(); + public abstract int getDisplayOptions(); + public abstract int getHeight(); + public abstract int getNavigationItemCount(); + public abstract int getNavigationMode(); + public abstract int getSelectedNavigationIndex(); + public abstract int getTabCount(); + public abstract void addOnMenuVisibilityListener(ActionBar.OnMenuVisibilityListener p0); + public abstract void addTab(ActionBar.Tab p0); + public abstract void addTab(ActionBar.Tab p0, boolean p1); + public abstract void addTab(ActionBar.Tab p0, int p1); + public abstract void addTab(ActionBar.Tab p0, int p1, boolean p2); + public abstract void hide(); + public abstract void removeAllTabs(); + public abstract void removeOnMenuVisibilityListener(ActionBar.OnMenuVisibilityListener p0); + public abstract void removeTab(ActionBar.Tab p0); + public abstract void removeTabAt(int p0); + public abstract void selectTab(ActionBar.Tab p0); + public abstract void setBackgroundDrawable(Drawable p0); + public abstract void setCustomView(View p0); + public abstract void setCustomView(View p0, ActionBar.LayoutParams p1); + public abstract void setCustomView(int p0); + public abstract void setDisplayHomeAsUpEnabled(boolean p0); + public abstract void setDisplayOptions(int p0); + public abstract void setDisplayOptions(int p0, int p1); + public abstract void setDisplayShowCustomEnabled(boolean p0); + public abstract void setDisplayShowHomeEnabled(boolean p0); + public abstract void setDisplayShowTitleEnabled(boolean p0); + public abstract void setDisplayUseLogoEnabled(boolean p0); + public abstract void setIcon(Drawable p0); + public abstract void setIcon(int p0); + public abstract void setListNavigationCallbacks(SpinnerAdapter p0, ActionBar.OnNavigationListener p1); + public abstract void setLogo(Drawable p0); + public abstract void setLogo(int p0); + public abstract void setNavigationMode(int p0); + public abstract void setSelectedNavigationItem(int p0); + public abstract void setSubtitle(CharSequence p0); + public abstract void setSubtitle(int p0); + public abstract void setTitle(CharSequence p0); + public abstract void setTitle(int p0); + public abstract void show(); + public boolean isHideOnContentScrollEnabled(){ return false; } + public float getElevation(){ return 0; } + public int getHideOffset(){ return 0; } + public static int DISPLAY_HOME_AS_UP = 0; + public static int DISPLAY_SHOW_CUSTOM = 0; + public static int DISPLAY_SHOW_HOME = 0; + public static int DISPLAY_SHOW_TITLE = 0; + public static int DISPLAY_USE_LOGO = 0; + public static int NAVIGATION_MODE_LIST = 0; + public static int NAVIGATION_MODE_STANDARD = 0; + public static int NAVIGATION_MODE_TABS = 0; + public void setElevation(float p0){} + public void setHideOffset(int p0){} + public void setHideOnContentScrollEnabled(boolean p0){} + public void setHomeActionContentDescription(CharSequence p0){} + public void setHomeActionContentDescription(int p0){} + public void setHomeAsUpIndicator(Drawable p0){} + public void setHomeAsUpIndicator(int p0){} + public void setHomeButtonEnabled(boolean p0){} + public void setSplitBackgroundDrawable(Drawable p0){} + public void setStackedBackgroundDrawable(Drawable p0){} + static public class LayoutParams extends ViewGroup.MarginLayoutParams + { + protected LayoutParams() {} + public LayoutParams(ActionBar.LayoutParams p0){} + public LayoutParams(Context p0, AttributeSet p1){} + public LayoutParams(ViewGroup.LayoutParams p0){} + public LayoutParams(int p0){} + public LayoutParams(int p0, int p1){} + public LayoutParams(int p0, int p1, int p2){} + public int gravity = 0; + } + static public interface OnMenuVisibilityListener + { + void onMenuVisibilityChanged(boolean p0); + } + static public interface OnNavigationListener + { + boolean onNavigationItemSelected(int p0, long p1); + } + static public interface TabListener + { + void onTabReselected(ActionBar.Tab p0, FragmentTransaction p1); + void onTabSelected(ActionBar.Tab p0, FragmentTransaction p1); + void onTabUnselected(ActionBar.Tab p0, FragmentTransaction p1); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/ActivityManager.java b/java/ql/test/stubs/google-android-9.0.0/android/app/ActivityManager.java new file mode 100644 index 00000000000..2d9bc359cf7 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/ActivityManager.java @@ -0,0 +1,215 @@ +// Generated automatically from android.app.ActivityManager for testing purposes + +package android.app; + +import android.app.Activity; +import android.app.ApplicationExitInfo; +import android.app.PendingIntent; +import android.app.TaskInfo; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ConfigurationInfo; +import android.graphics.Bitmap; +import android.os.Bundle; +import android.os.Debug; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Size; +import java.io.FileDescriptor; +import java.util.List; + +public class ActivityManager +{ + public ConfigurationInfo getDeviceConfigurationInfo(){ return null; } + public Debug.MemoryInfo[] getProcessMemoryInfo(int[] p0){ return null; } + public List getAppTasks(){ return null; } + public List getProcessesInErrorState(){ return null; } + public List getRecentTasks(int p0, int p1){ return null; } + public List getRunningAppProcesses(){ return null; } + public List getRunningServices(int p0){ return null; } + public List getRunningTasks(int p0){ return null; } + public List getHistoricalProcessExitReasons(String p0, int p1, int p2){ return null; } + public PendingIntent getRunningServiceControlPanel(ComponentName p0){ return null; } + public Size getAppTaskThumbnailSize(){ return null; } + public boolean clearApplicationUserData(){ return false; } + public boolean isActivityStartAllowedOnDisplay(Context p0, int p1, Intent p2){ return false; } + public boolean isBackgroundRestricted(){ return false; } + public boolean isInLockTaskMode(){ return false; } + public boolean isLowRamDevice(){ return false; } + public int addAppTask(Activity p0, Intent p1, ActivityManager.TaskDescription p2, Bitmap p3){ return 0; } + public int getLargeMemoryClass(){ return 0; } + public int getLauncherLargeIconDensity(){ return 0; } + public int getLauncherLargeIconSize(){ return 0; } + public int getLockTaskModeState(){ return 0; } + public int getMemoryClass(){ return 0; } + public static String ACTION_REPORT_HEAP_LIMIT = null; + public static String META_HOME_ALTERNATE = null; + public static boolean isLowMemoryKillReportSupported(){ return false; } + public static boolean isRunningInTestHarness(){ return false; } + public static boolean isRunningInUserTestHarness(){ return false; } + public static boolean isUserAMonkey(){ return false; } + public static int LOCK_TASK_MODE_LOCKED = 0; + public static int LOCK_TASK_MODE_NONE = 0; + public static int LOCK_TASK_MODE_PINNED = 0; + public static int MOVE_TASK_NO_USER_ACTION = 0; + public static int MOVE_TASK_WITH_HOME = 0; + public static int RECENT_IGNORE_UNAVAILABLE = 0; + public static int RECENT_WITH_EXCLUDED = 0; + public static void getMyMemoryState(ActivityManager.RunningAppProcessInfo p0){} + public static void setVrThread(int p0){} + public void appNotResponding(String p0){} + public void clearWatchHeapLimit(){} + public void dumpPackageState(FileDescriptor p0, String p1){} + public void getMemoryInfo(ActivityManager.MemoryInfo p0){} + public void killBackgroundProcesses(String p0){} + public void moveTaskToFront(int p0, int p1){} + public void moveTaskToFront(int p0, int p1, Bundle p2){} + public void restartPackage(String p0){} + public void setProcessStateSummary(byte[] p0){} + public void setWatchHeapLimit(long p0){} + static public class AppTask + { + public ActivityManager.RecentTaskInfo getTaskInfo(){ return null; } + public void finishAndRemoveTask(){} + public void moveToFront(){} + public void setExcludeFromRecents(boolean p0){} + public void startActivity(Context p0, Intent p1, Bundle p2){} + } + static public class MemoryInfo implements Parcelable + { + public MemoryInfo(){} + public boolean lowMemory = false; + public int describeContents(){ return 0; } + public long availMem = 0; + public long threshold = 0; + public long totalMem = 0; + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class ProcessErrorStateInfo implements Parcelable + { + public ProcessErrorStateInfo(){} + public String longMsg = null; + public String processName = null; + public String shortMsg = null; + public String stackTrace = null; + public String tag = null; + public byte[] crashData = null; + public int condition = 0; + public int describeContents(){ return 0; } + public int pid = 0; + public int uid = 0; + public static Parcelable.Creator CREATOR = null; + public static int CRASHED = 0; + public static int NOT_RESPONDING = 0; + public static int NO_ERROR = 0; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class RecentTaskInfo extends TaskInfo implements Parcelable + { + public CharSequence description = null; + public RecentTaskInfo(){} + public int affiliatedTaskId = 0; + public int describeContents(){ return 0; } + public int id = 0; + public int persistentId = 0; + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class RunningAppProcessInfo implements Parcelable + { + public ComponentName importanceReasonComponent = null; + public RunningAppProcessInfo(){} + public RunningAppProcessInfo(String p0, int p1, String[] p2){} + public String processName = null; + public String[] pkgList = null; + public int describeContents(){ return 0; } + public int importance = 0; + public int importanceReasonCode = 0; + public int importanceReasonPid = 0; + public int lastTrimLevel = 0; + public int lru = 0; + public int pid = 0; + public int uid = 0; + public static Parcelable.Creator CREATOR = null; + public static int IMPORTANCE_BACKGROUND = 0; + public static int IMPORTANCE_CACHED = 0; + public static int IMPORTANCE_CANT_SAVE_STATE = 0; + public static int IMPORTANCE_EMPTY = 0; + public static int IMPORTANCE_FOREGROUND = 0; + public static int IMPORTANCE_FOREGROUND_SERVICE = 0; + public static int IMPORTANCE_GONE = 0; + public static int IMPORTANCE_PERCEPTIBLE = 0; + public static int IMPORTANCE_PERCEPTIBLE_PRE_26 = 0; + public static int IMPORTANCE_SERVICE = 0; + public static int IMPORTANCE_TOP_SLEEPING = 0; + public static int IMPORTANCE_TOP_SLEEPING_PRE_28 = 0; + public static int IMPORTANCE_VISIBLE = 0; + public static int REASON_PROVIDER_IN_USE = 0; + public static int REASON_SERVICE_IN_USE = 0; + public static int REASON_UNKNOWN = 0; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class RunningServiceInfo implements Parcelable + { + public ComponentName service = null; + public RunningServiceInfo(){} + public String clientPackage = null; + public String process = null; + public boolean foreground = false; + public boolean started = false; + public int clientCount = 0; + public int clientLabel = 0; + public int crashCount = 0; + public int describeContents(){ return 0; } + public int flags = 0; + public int pid = 0; + public int uid = 0; + public long activeSince = 0; + public long lastActivityTime = 0; + public long restarting = 0; + public static Parcelable.Creator CREATOR = null; + public static int FLAG_FOREGROUND = 0; + public static int FLAG_PERSISTENT_PROCESS = 0; + public static int FLAG_STARTED = 0; + public static int FLAG_SYSTEM_PROCESS = 0; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class RunningTaskInfo extends TaskInfo implements Parcelable + { + public Bitmap thumbnail = null; + public CharSequence description = null; + public RunningTaskInfo(){} + public int describeContents(){ return 0; } + public int id = 0; + public int numRunning = 0; + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } + static public class TaskDescription implements Parcelable + { + public Bitmap getIcon(){ return null; } + public String getLabel(){ return null; } + public String toString(){ return null; } + public TaskDescription(){} + public TaskDescription(ActivityManager.TaskDescription p0){} + public TaskDescription(String p0){} + public TaskDescription(String p0, Bitmap p1){} + public TaskDescription(String p0, Bitmap p1, int p2){} + public TaskDescription(String p0, int p1){} + public TaskDescription(String p0, int p1, int p2){} + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getPrimaryColor(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/AlertDialog.java b/java/ql/test/stubs/google-android-9.0.0/android/app/AlertDialog.java new file mode 100644 index 00000000000..530cccf7bc1 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/AlertDialog.java @@ -0,0 +1,95 @@ +// Generated automatically from android.app.AlertDialog for testing purposes + +package android.app; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.database.Cursor; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Message; +import android.view.KeyEvent; +import android.view.View; +import android.widget.Adapter; +import android.widget.AdapterView; +import android.widget.Button; +import android.widget.ListAdapter; +import android.widget.ListView; + +public class AlertDialog extends Dialog implements DialogInterface +{ + protected AlertDialog() {} + protected AlertDialog(Context p0){} + protected AlertDialog(Context p0, boolean p1, DialogInterface.OnCancelListener p2){} + protected AlertDialog(Context p0, int p1){} + protected void onCreate(Bundle p0){} + public Button getButton(int p0){ return null; } + public ListView getListView(){ return null; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public static int THEME_DEVICE_DEFAULT_DARK = 0; + public static int THEME_DEVICE_DEFAULT_LIGHT = 0; + public static int THEME_HOLO_DARK = 0; + public static int THEME_HOLO_LIGHT = 0; + public static int THEME_TRADITIONAL = 0; + public void setButton(CharSequence p0, DialogInterface.OnClickListener p1){} + public void setButton(CharSequence p0, Message p1){} + public void setButton(int p0, CharSequence p1, DialogInterface.OnClickListener p2){} + public void setButton(int p0, CharSequence p1, Message p2){} + public void setButton2(CharSequence p0, DialogInterface.OnClickListener p1){} + public void setButton2(CharSequence p0, Message p1){} + public void setButton3(CharSequence p0, DialogInterface.OnClickListener p1){} + public void setButton3(CharSequence p0, Message p1){} + public void setCustomTitle(View p0){} + public void setIcon(Drawable p0){} + public void setIcon(int p0){} + public void setIconAttribute(int p0){} + public void setInverseBackgroundForced(boolean p0){} + public void setMessage(CharSequence p0){} + public void setTitle(CharSequence p0){} + public void setView(View p0){} + public void setView(View p0, int p1, int p2, int p3, int p4){} + static public class Builder + { + protected Builder() {} + public AlertDialog create(){ return null; } + public AlertDialog show(){ return null; } + public AlertDialog.Builder setAdapter(ListAdapter p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setCancelable(boolean p0){ return null; } + public AlertDialog.Builder setCursor(Cursor p0, DialogInterface.OnClickListener p1, String p2){ return null; } + public AlertDialog.Builder setCustomTitle(View p0){ return null; } + public AlertDialog.Builder setIcon(Drawable p0){ return null; } + public AlertDialog.Builder setIcon(int p0){ return null; } + public AlertDialog.Builder setIconAttribute(int p0){ return null; } + public AlertDialog.Builder setInverseBackgroundForced(boolean p0){ return null; } + public AlertDialog.Builder setItems(CharSequence[] p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setItems(int p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setMessage(CharSequence p0){ return null; } + public AlertDialog.Builder setMessage(int p0){ return null; } + public AlertDialog.Builder setMultiChoiceItems(CharSequence[] p0, boolean[] p1, DialogInterface.OnMultiChoiceClickListener p2){ return null; } + public AlertDialog.Builder setMultiChoiceItems(Cursor p0, String p1, String p2, DialogInterface.OnMultiChoiceClickListener p3){ return null; } + public AlertDialog.Builder setMultiChoiceItems(int p0, boolean[] p1, DialogInterface.OnMultiChoiceClickListener p2){ return null; } + public AlertDialog.Builder setNegativeButton(CharSequence p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setNegativeButton(int p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setNeutralButton(CharSequence p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setNeutralButton(int p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setOnCancelListener(DialogInterface.OnCancelListener p0){ return null; } + public AlertDialog.Builder setOnDismissListener(DialogInterface.OnDismissListener p0){ return null; } + public AlertDialog.Builder setOnItemSelectedListener(AdapterView.OnItemSelectedListener p0){ return null; } + public AlertDialog.Builder setOnKeyListener(DialogInterface.OnKeyListener p0){ return null; } + public AlertDialog.Builder setPositiveButton(CharSequence p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setPositiveButton(int p0, DialogInterface.OnClickListener p1){ return null; } + public AlertDialog.Builder setSingleChoiceItems(CharSequence[] p0, int p1, DialogInterface.OnClickListener p2){ return null; } + public AlertDialog.Builder setSingleChoiceItems(Cursor p0, int p1, String p2, DialogInterface.OnClickListener p3){ return null; } + public AlertDialog.Builder setSingleChoiceItems(ListAdapter p0, int p1, DialogInterface.OnClickListener p2){ return null; } + public AlertDialog.Builder setSingleChoiceItems(int p0, int p1, DialogInterface.OnClickListener p2){ return null; } + public AlertDialog.Builder setTitle(CharSequence p0){ return null; } + public AlertDialog.Builder setTitle(int p0){ return null; } + public AlertDialog.Builder setView(View p0){ return null; } + public AlertDialog.Builder setView(int p0){ return null; } + public Builder(Context p0){} + public Builder(Context p0, int p1){} + public Context getContext(){ return null; } + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Application.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Application.java new file mode 100644 index 00000000000..e9199a2da83 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Application.java @@ -0,0 +1,55 @@ +// Generated automatically from android.app.Application for testing purposes + +package android.app; + +import android.app.Activity; +import android.content.ComponentCallbacks2; +import android.content.ComponentCallbacks; +import android.content.ContextWrapper; +import android.content.res.Configuration; +import android.os.Bundle; + +public class Application extends ContextWrapper implements ComponentCallbacks2 +{ + public Application(){} + public static String getProcessName(){ return null; } + public void onConfigurationChanged(Configuration p0){} + public void onCreate(){} + public void onLowMemory(){} + public void onTerminate(){} + public void onTrimMemory(int p0){} + public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks p0){} + public void registerComponentCallbacks(ComponentCallbacks p0){} + public void registerOnProvideAssistDataListener(Application.OnProvideAssistDataListener p0){} + public void unregisterActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks p0){} + public void unregisterComponentCallbacks(ComponentCallbacks p0){} + public void unregisterOnProvideAssistDataListener(Application.OnProvideAssistDataListener p0){} + static public interface ActivityLifecycleCallbacks + { + default void onActivityPostCreated(Activity p0, Bundle p1){} + default void onActivityPostDestroyed(Activity p0){} + default void onActivityPostPaused(Activity p0){} + default void onActivityPostResumed(Activity p0){} + default void onActivityPostSaveInstanceState(Activity p0, Bundle p1){} + default void onActivityPostStarted(Activity p0){} + default void onActivityPostStopped(Activity p0){} + default void onActivityPreCreated(Activity p0, Bundle p1){} + default void onActivityPreDestroyed(Activity p0){} + default void onActivityPrePaused(Activity p0){} + default void onActivityPreResumed(Activity p0){} + default void onActivityPreSaveInstanceState(Activity p0, Bundle p1){} + default void onActivityPreStarted(Activity p0){} + default void onActivityPreStopped(Activity p0){} + void onActivityCreated(Activity p0, Bundle p1); + void onActivityDestroyed(Activity p0); + void onActivityPaused(Activity p0); + void onActivityResumed(Activity p0); + void onActivitySaveInstanceState(Activity p0, Bundle p1); + void onActivityStarted(Activity p0); + void onActivityStopped(Activity p0); + } + static public interface OnProvideAssistDataListener + { + void onProvideAssistData(Activity p0, Bundle p1); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/ApplicationExitInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/app/ApplicationExitInfo.java new file mode 100644 index 00000000000..6f412aa122a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/ApplicationExitInfo.java @@ -0,0 +1,47 @@ +// Generated automatically from android.app.ApplicationExitInfo for testing purposes + +package android.app; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; +import java.io.InputStream; + +public class ApplicationExitInfo implements Parcelable +{ + public InputStream getTraceInputStream(){ return null; } + public String getDescription(){ return null; } + public String getProcessName(){ return null; } + public String toString(){ return null; } + public UserHandle getUserHandle(){ return null; } + public boolean equals(Object p0){ return false; } + public byte[] getProcessStateSummary(){ return null; } + public int describeContents(){ return 0; } + public int getDefiningUid(){ return 0; } + public int getImportance(){ return 0; } + public int getPackageUid(){ return 0; } + public int getPid(){ return 0; } + public int getRealUid(){ return 0; } + public int getReason(){ return 0; } + public int getStatus(){ return 0; } + public int hashCode(){ return 0; } + public long getPss(){ return 0; } + public long getRss(){ return 0; } + public long getTimestamp(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int REASON_ANR = 0; + public static int REASON_CRASH = 0; + public static int REASON_CRASH_NATIVE = 0; + public static int REASON_DEPENDENCY_DIED = 0; + public static int REASON_EXCESSIVE_RESOURCE_USAGE = 0; + public static int REASON_EXIT_SELF = 0; + public static int REASON_INITIALIZATION_FAILURE = 0; + public static int REASON_LOW_MEMORY = 0; + public static int REASON_OTHER = 0; + public static int REASON_PERMISSION_CHANGE = 0; + public static int REASON_SIGNALED = 0; + public static int REASON_UNKNOWN = 0; + public static int REASON_USER_REQUESTED = 0; + public static int REASON_USER_STOPPED = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Dialog.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Dialog.java new file mode 100644 index 00000000000..35c4b8bb393 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Dialog.java @@ -0,0 +1,121 @@ +// Generated automatically from android.app.Dialog for testing purposes + +package android.app; + +import android.app.ActionBar; +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.os.Message; +import android.view.ActionMode; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.SearchEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; + +public class Dialog implements DialogInterface, KeyEvent.Callback, View.OnCreateContextMenuListener, Window.Callback +{ + protected Dialog() {} + protected Dialog(Context p0, boolean p1, DialogInterface.OnCancelListener p2){} + protected void onCreate(Bundle p0){} + protected void onStart(){} + protected void onStop(){} + public T findViewById(int p0){ return null; } + public ActionBar getActionBar(){ return null; } + public ActionMode onWindowStartingActionMode(ActionMode.Callback p0){ return null; } + public ActionMode onWindowStartingActionMode(ActionMode.Callback p0, int p1){ return null; } + public Bundle onSaveInstanceState(){ return null; } + public Dialog(Context p0){} + public Dialog(Context p0, int p1){} + public LayoutInflater getLayoutInflater(){ return null; } + public View getCurrentFocus(){ return null; } + public View onCreatePanelView(int p0){ return null; } + public Window getWindow(){ return null; } + public boolean dispatchGenericMotionEvent(MotionEvent p0){ return false; } + public boolean dispatchKeyEvent(KeyEvent p0){ return false; } + public boolean dispatchKeyShortcutEvent(KeyEvent p0){ return false; } + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent p0){ return false; } + public boolean dispatchTouchEvent(MotionEvent p0){ return false; } + public boolean dispatchTrackballEvent(MotionEvent p0){ return false; } + public boolean isShowing(){ return false; } + public boolean onContextItemSelected(MenuItem p0){ return false; } + public boolean onCreateOptionsMenu(Menu p0){ return false; } + public boolean onCreatePanelMenu(int p0, Menu p1){ return false; } + public boolean onGenericMotionEvent(MotionEvent p0){ return false; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyLongPress(int p0, KeyEvent p1){ return false; } + public boolean onKeyMultiple(int p0, int p1, KeyEvent p2){ return false; } + public boolean onKeyShortcut(int p0, KeyEvent p1){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public boolean onMenuItemSelected(int p0, MenuItem p1){ return false; } + public boolean onMenuOpened(int p0, Menu p1){ return false; } + public boolean onOptionsItemSelected(MenuItem p0){ return false; } + public boolean onPrepareOptionsMenu(Menu p0){ return false; } + public boolean onPreparePanel(int p0, View p1, Menu p2){ return false; } + public boolean onSearchRequested(){ return false; } + public boolean onSearchRequested(SearchEvent p0){ return false; } + public boolean onTouchEvent(MotionEvent p0){ return false; } + public boolean onTrackballEvent(MotionEvent p0){ return false; } + public final T requireViewById(int p0){ return null; } + public final Activity getOwnerActivity(){ return null; } + public final Context getContext(){ return null; } + public final SearchEvent getSearchEvent(){ return null; } + public final boolean requestWindowFeature(int p0){ return false; } + public final int getVolumeControlStream(){ return 0; } + public final void setFeatureDrawable(int p0, Drawable p1){} + public final void setFeatureDrawableAlpha(int p0, int p1){} + public final void setFeatureDrawableResource(int p0, int p1){} + public final void setFeatureDrawableUri(int p0, Uri p1){} + public final void setOwnerActivity(Activity p0){} + public final void setVolumeControlStream(int p0){} + public void addContentView(View p0, ViewGroup.LayoutParams p1){} + public void cancel(){} + public void closeOptionsMenu(){} + public void create(){} + public void dismiss(){} + public void hide(){} + public void invalidateOptionsMenu(){} + public void onActionModeFinished(ActionMode p0){} + public void onActionModeStarted(ActionMode p0){} + public void onAttachedToWindow(){} + public void onBackPressed(){} + public void onContentChanged(){} + public void onContextMenuClosed(Menu p0){} + public void onCreateContextMenu(ContextMenu p0, View p1, ContextMenu.ContextMenuInfo p2){} + public void onDetachedFromWindow(){} + public void onOptionsMenuClosed(Menu p0){} + public void onPanelClosed(int p0, Menu p1){} + public void onRestoreInstanceState(Bundle p0){} + public void onWindowAttributesChanged(WindowManager.LayoutParams p0){} + public void onWindowFocusChanged(boolean p0){} + public void openContextMenu(View p0){} + public void openOptionsMenu(){} + public void registerForContextMenu(View p0){} + public void setCancelMessage(Message p0){} + public void setCancelable(boolean p0){} + public void setCanceledOnTouchOutside(boolean p0){} + public void setContentView(View p0){} + public void setContentView(View p0, ViewGroup.LayoutParams p1){} + public void setContentView(int p0){} + public void setDismissMessage(Message p0){} + public void setOnCancelListener(DialogInterface.OnCancelListener p0){} + public void setOnDismissListener(DialogInterface.OnDismissListener p0){} + public void setOnKeyListener(DialogInterface.OnKeyListener p0){} + public void setOnShowListener(DialogInterface.OnShowListener p0){} + public void setTitle(CharSequence p0){} + public void setTitle(int p0){} + public void show(){} + public void takeKeyEvents(boolean p0){} + public void unregisterForContextMenu(View p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/DirectAction.java b/java/ql/test/stubs/google-android-9.0.0/android/app/DirectAction.java new file mode 100644 index 00000000000..7eeaa9eac66 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/DirectAction.java @@ -0,0 +1,20 @@ +// Generated automatically from android.app.DirectAction for testing purposes + +package android.app; + +import android.content.LocusId; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class DirectAction implements Parcelable +{ + public Bundle getExtras(){ return null; } + public LocusId getLocusId(){ return null; } + public String getId(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java b/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java index 5e5f71dcd25..41969f5d877 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/Fragment.java @@ -1,82 +1,148 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ +// Generated automatically from android.app.Fragment for testing purposes package android.app; -import android.annotation.Nullable; +import android.animation.Animator; +import android.app.Activity; +import android.app.FragmentManager; +import android.app.LoaderManager; +import android.app.SharedElementCallback; import android.content.ComponentCallbacks2; import android.content.Context; import android.content.Intent; +import android.content.IntentSender; import android.content.res.Configuration; +import android.content.res.Resources; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.transition.Transition; +import android.util.AttributeSet; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import java.io.FileDescriptor; +import java.io.PrintWriter; -public class Fragment implements ComponentCallbacks2 { - public static class SavedState implements Parcelable { - @Override - public int describeContents() { - return 0; +public class Fragment implements ComponentCallbacks2, View.OnCreateContextMenuListener +{ + public Animator onCreateAnimator(int p0, boolean p1, int p2){ return null; } + public Context getContext(){ return null; } + public Fragment(){} + public LayoutInflater onGetLayoutInflater(Bundle p0){ return null; } + public LoaderManager getLoaderManager(){ return null; } + public String toString(){ return null; } + public Transition getEnterTransition(){ return null; } + public Transition getExitTransition(){ return null; } + public Transition getReenterTransition(){ return null; } + public Transition getReturnTransition(){ return null; } + public Transition getSharedElementEnterTransition(){ return null; } + public Transition getSharedElementReturnTransition(){ return null; } + public View getView(){ return null; } + public View onCreateView(LayoutInflater p0, ViewGroup p1, Bundle p2){ return null; } + public boolean getAllowEnterTransitionOverlap(){ return false; } + public boolean getAllowReturnTransitionOverlap(){ return false; } + public boolean getUserVisibleHint(){ return false; } + public boolean onContextItemSelected(MenuItem p0){ return false; } + public boolean onOptionsItemSelected(MenuItem p0){ return false; } + public boolean shouldShowRequestPermissionRationale(String p0){ return false; } + public final Activity getActivity(){ return null; } + public final Bundle getArguments(){ return null; } + public final CharSequence getText(int p0){ return null; } + public final Fragment getParentFragment(){ return null; } + public final Fragment getTargetFragment(){ return null; } + public final FragmentManager getChildFragmentManager(){ return null; } + public final FragmentManager getFragmentManager(){ return null; } + public final LayoutInflater getLayoutInflater(){ return null; } + public final Object getHost(){ return null; } + public final Resources getResources(){ return null; } + public final String getString(int p0){ return null; } + public final String getString(int p0, Object... p1){ return null; } + public final String getTag(){ return null; } + public final boolean equals(Object p0){ return false; } + public final boolean getRetainInstance(){ return false; } + public final boolean isAdded(){ return false; } + public final boolean isDetached(){ return false; } + public final boolean isHidden(){ return false; } + public final boolean isInLayout(){ return false; } + public final boolean isRemoving(){ return false; } + public final boolean isResumed(){ return false; } + public final boolean isStateSaved(){ return false; } + public final boolean isVisible(){ return false; } + public final int getId(){ return 0; } + public final int getTargetRequestCode(){ return 0; } + public final int hashCode(){ return 0; } + public final void requestPermissions(String[] p0, int p1){} + public static Fragment instantiate(Context p0, String p1){ return null; } + public static Fragment instantiate(Context p0, String p1, Bundle p2){ return null; } + public void dump(String p0, FileDescriptor p1, PrintWriter p2, String[] p3){} + public void onActivityCreated(Bundle p0){} + public void onActivityResult(int p0, int p1, Intent p2){} + public void onAttach(Activity p0){} + public void onAttach(Context p0){} + public void onAttachFragment(Fragment p0){} + public void onConfigurationChanged(Configuration p0){} + public void onCreate(Bundle p0){} + public void onCreateContextMenu(ContextMenu p0, View p1, ContextMenu.ContextMenuInfo p2){} + public void onCreateOptionsMenu(Menu p0, MenuInflater p1){} + public void onDestroy(){} + public void onDestroyOptionsMenu(){} + public void onDestroyView(){} + public void onDetach(){} + public void onHiddenChanged(boolean p0){} + public void onInflate(Activity p0, AttributeSet p1, Bundle p2){} + public void onInflate(AttributeSet p0, Bundle p1){} + public void onInflate(Context p0, AttributeSet p1, Bundle p2){} + public void onLowMemory(){} + public void onMultiWindowModeChanged(boolean p0){} + public void onMultiWindowModeChanged(boolean p0, Configuration p1){} + public void onOptionsMenuClosed(Menu p0){} + public void onPause(){} + public void onPictureInPictureModeChanged(boolean p0){} + public void onPictureInPictureModeChanged(boolean p0, Configuration p1){} + public void onPrepareOptionsMenu(Menu p0){} + public void onRequestPermissionsResult(int p0, String[] p1, int[] p2){} + public void onResume(){} + public void onSaveInstanceState(Bundle p0){} + public void onStart(){} + public void onStop(){} + public void onTrimMemory(int p0){} + public void onViewCreated(View p0, Bundle p1){} + public void onViewStateRestored(Bundle p0){} + public void postponeEnterTransition(){} + public void registerForContextMenu(View p0){} + public void setAllowEnterTransitionOverlap(boolean p0){} + public void setAllowReturnTransitionOverlap(boolean p0){} + public void setArguments(Bundle p0){} + public void setEnterSharedElementCallback(SharedElementCallback p0){} + public void setEnterTransition(Transition p0){} + public void setExitSharedElementCallback(SharedElementCallback p0){} + public void setExitTransition(Transition p0){} + public void setHasOptionsMenu(boolean p0){} + public void setInitialSavedState(Fragment.SavedState p0){} + public void setMenuVisibility(boolean p0){} + public void setReenterTransition(Transition p0){} + public void setRetainInstance(boolean p0){} + public void setReturnTransition(Transition p0){} + public void setSharedElementEnterTransition(Transition p0){} + public void setSharedElementReturnTransition(Transition p0){} + public void setTargetFragment(Fragment p0, int p1){} + public void setUserVisibleHint(boolean p0){} + public void startActivity(Intent p0){} + public void startActivity(Intent p0, Bundle p1){} + public void startActivityForResult(Intent p0, int p1){} + public void startActivityForResult(Intent p0, int p1, Bundle p2){} + public void startIntentSenderForResult(IntentSender p0, int p1, Intent p2, int p3, int p4, int p5, Bundle p6){} + public void startPostponedEnterTransition(){} + public void unregisterForContextMenu(View p0){} + static public class SavedState implements Parcelable + { + public int describeContents(){ return 0; } + public static Parcelable.ClassLoaderCreator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} } - - @Override - public void writeToParcel(Parcel dest, int flags) {} - - } - - static public class InstantiationException { - public InstantiationException(String msg, Exception cause) {} - } - - - public Fragment() {} - - public static Fragment instantiate(Context context, String fname) { - return null; - } - - public static Fragment instantiate(Context context, String fname, @Nullable Bundle args) { - return null; - } - - @Override - final public boolean equals(Object o) { - return false; - } - - @Override - final public int hashCode() { - return 0; - } - - @Override - public String toString() { - return null; - } - - @Override - public void onConfigurationChanged(Configuration p0) {} - - @Override - public void onLowMemory() {} - - @Override - public void onTrimMemory(int p0) {} - - public void startActivityForResult(Intent intent, int requestCode) {} - - public void startActivityForResult(Intent intent, int requestCode, Bundle options) {} - - public void onActivityResult(int requestCode, int resultCode, Intent data) {} } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentManager.java b/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentManager.java new file mode 100644 index 00000000000..0d62560b685 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentManager.java @@ -0,0 +1,75 @@ +// Generated automatically from android.app.FragmentManager for testing purposes + +package android.app; + +import android.app.Fragment; +import android.app.FragmentTransaction; +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.List; + +abstract public class FragmentManager +{ + abstract static public class FragmentLifecycleCallbacks + { + public FragmentLifecycleCallbacks(){} + public void onFragmentActivityCreated(FragmentManager p0, Fragment p1, Bundle p2){} + public void onFragmentAttached(FragmentManager p0, Fragment p1, Context p2){} + public void onFragmentCreated(FragmentManager p0, Fragment p1, Bundle p2){} + public void onFragmentDestroyed(FragmentManager p0, Fragment p1){} + public void onFragmentDetached(FragmentManager p0, Fragment p1){} + public void onFragmentPaused(FragmentManager p0, Fragment p1){} + public void onFragmentPreAttached(FragmentManager p0, Fragment p1, Context p2){} + public void onFragmentPreCreated(FragmentManager p0, Fragment p1, Bundle p2){} + public void onFragmentResumed(FragmentManager p0, Fragment p1){} + public void onFragmentSaveInstanceState(FragmentManager p0, Fragment p1, Bundle p2){} + public void onFragmentStarted(FragmentManager p0, Fragment p1){} + public void onFragmentStopped(FragmentManager p0, Fragment p1){} + public void onFragmentViewCreated(FragmentManager p0, Fragment p1, View p2, Bundle p3){} + public void onFragmentViewDestroyed(FragmentManager p0, Fragment p1){} + } + public FragmentManager(){} + public abstract Fragment findFragmentById(int p0); + public abstract Fragment findFragmentByTag(String p0); + public abstract Fragment getFragment(Bundle p0, String p1); + public abstract Fragment getPrimaryNavigationFragment(); + public abstract Fragment.SavedState saveFragmentInstanceState(Fragment p0); + public abstract FragmentManager.BackStackEntry getBackStackEntryAt(int p0); + public abstract FragmentTransaction beginTransaction(); + public abstract List getFragments(); + public abstract boolean executePendingTransactions(); + public abstract boolean isDestroyed(); + public abstract boolean isStateSaved(); + public abstract boolean popBackStackImmediate(); + public abstract boolean popBackStackImmediate(String p0, int p1); + public abstract boolean popBackStackImmediate(int p0, int p1); + public abstract int getBackStackEntryCount(); + public abstract void addOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener p0); + public abstract void dump(String p0, FileDescriptor p1, PrintWriter p2, String[] p3); + public abstract void popBackStack(); + public abstract void popBackStack(String p0, int p1); + public abstract void popBackStack(int p0, int p1); + public abstract void putFragment(Bundle p0, String p1, Fragment p2); + public abstract void registerFragmentLifecycleCallbacks(FragmentManager.FragmentLifecycleCallbacks p0, boolean p1); + public abstract void removeOnBackStackChangedListener(FragmentManager.OnBackStackChangedListener p0); + public abstract void unregisterFragmentLifecycleCallbacks(FragmentManager.FragmentLifecycleCallbacks p0); + public static int POP_BACK_STACK_INCLUSIVE = 0; + public static void enableDebugLogging(boolean p0){} + public void invalidateOptionsMenu(){} + static public interface BackStackEntry + { + CharSequence getBreadCrumbShortTitle(); + CharSequence getBreadCrumbTitle(); + String getName(); + int getBreadCrumbShortTitleRes(); + int getBreadCrumbTitleRes(); + int getId(); + } + static public interface OnBackStackChangedListener + { + void onBackStackChanged(); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentTransaction.java b/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentTransaction.java new file mode 100644 index 00000000000..762dc14091c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/FragmentTransaction.java @@ -0,0 +1,48 @@ +// Generated automatically from android.app.FragmentTransaction for testing purposes + +package android.app; + +import android.app.Fragment; +import android.view.View; + +abstract public class FragmentTransaction +{ + public FragmentTransaction(){} + public abstract FragmentTransaction add(Fragment p0, String p1); + public abstract FragmentTransaction add(int p0, Fragment p1); + public abstract FragmentTransaction add(int p0, Fragment p1, String p2); + public abstract FragmentTransaction addSharedElement(View p0, String p1); + public abstract FragmentTransaction addToBackStack(String p0); + public abstract FragmentTransaction attach(Fragment p0); + public abstract FragmentTransaction detach(Fragment p0); + public abstract FragmentTransaction disallowAddToBackStack(); + public abstract FragmentTransaction hide(Fragment p0); + public abstract FragmentTransaction remove(Fragment p0); + public abstract FragmentTransaction replace(int p0, Fragment p1); + public abstract FragmentTransaction replace(int p0, Fragment p1, String p2); + public abstract FragmentTransaction runOnCommit(Runnable p0); + public abstract FragmentTransaction setBreadCrumbShortTitle(CharSequence p0); + public abstract FragmentTransaction setBreadCrumbShortTitle(int p0); + public abstract FragmentTransaction setBreadCrumbTitle(CharSequence p0); + public abstract FragmentTransaction setBreadCrumbTitle(int p0); + public abstract FragmentTransaction setCustomAnimations(int p0, int p1); + public abstract FragmentTransaction setCustomAnimations(int p0, int p1, int p2, int p3); + public abstract FragmentTransaction setPrimaryNavigationFragment(Fragment p0); + public abstract FragmentTransaction setReorderingAllowed(boolean p0); + public abstract FragmentTransaction setTransition(int p0); + public abstract FragmentTransaction setTransitionStyle(int p0); + public abstract FragmentTransaction show(Fragment p0); + public abstract boolean isAddToBackStackAllowed(); + public abstract boolean isEmpty(); + public abstract int commit(); + public abstract int commitAllowingStateLoss(); + public abstract void commitNow(); + public abstract void commitNowAllowingStateLoss(); + public static int TRANSIT_ENTER_MASK = 0; + public static int TRANSIT_EXIT_MASK = 0; + public static int TRANSIT_FRAGMENT_CLOSE = 0; + public static int TRANSIT_FRAGMENT_FADE = 0; + public static int TRANSIT_FRAGMENT_OPEN = 0; + public static int TRANSIT_NONE = 0; + public static int TRANSIT_UNSET = 0; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/LoaderManager.java b/java/ql/test/stubs/google-android-9.0.0/android/app/LoaderManager.java new file mode 100644 index 00000000000..1c94d938590 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/LoaderManager.java @@ -0,0 +1,25 @@ +// Generated automatically from android.app.LoaderManager for testing purposes + +package android.app; + +import android.content.Loader; +import android.os.Bundle; +import java.io.FileDescriptor; +import java.io.PrintWriter; + +abstract public class LoaderManager +{ + public LoaderManager(){} + public abstract Loader getLoader(int p0); + public abstract Loader initLoader(int p0, Bundle p1, LoaderManager.LoaderCallbacks p2); + public abstract Loader restartLoader(int p0, Bundle p1, LoaderManager.LoaderCallbacks p2); + public abstract void destroyLoader(int p0); + public abstract void dump(String p0, FileDescriptor p1, PrintWriter p2, String[] p3); + public static void enableDebugLogging(boolean p0){} + static public interface LoaderCallbacks + { + Loader onCreateLoader(int p0, Bundle p1); + void onLoadFinished(Loader p0, D p1); + void onLoaderReset(Loader p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureParams.java b/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureParams.java new file mode 100644 index 00000000000..4186fa98993 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureParams.java @@ -0,0 +1,16 @@ +// Generated automatically from android.app.PictureInPictureParams for testing purposes + +package android.app; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PictureInPictureParams implements Parcelable +{ + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureUiState.java b/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureUiState.java new file mode 100644 index 00000000000..9e2485a6f3a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/PictureInPictureUiState.java @@ -0,0 +1,16 @@ +// Generated automatically from android.app.PictureInPictureUiState for testing purposes + +package android.app; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PictureInPictureUiState implements Parcelable +{ + public boolean equals(Object p0){ return false; } + public boolean isStashed(){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/RemoteAction.java b/java/ql/test/stubs/google-android-9.0.0/android/app/RemoteAction.java new file mode 100644 index 00000000000..58dec2cc81a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/RemoteAction.java @@ -0,0 +1,30 @@ +// Generated automatically from android.app.RemoteAction for testing purposes + +package android.app; + +import android.app.PendingIntent; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; +import java.io.PrintWriter; + +public class RemoteAction implements Parcelable +{ + protected RemoteAction() {} + public CharSequence getContentDescription(){ return null; } + public CharSequence getTitle(){ return null; } + public Icon getIcon(){ return null; } + public PendingIntent getActionIntent(){ return null; } + public RemoteAction clone(){ return null; } + public RemoteAction(Icon p0, CharSequence p1, CharSequence p2, PendingIntent p3){} + public boolean equals(Object p0){ return false; } + public boolean isEnabled(){ return false; } + public boolean shouldShowIcon(){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void dump(String p0, PrintWriter p1){} + public void setEnabled(boolean p0){} + public void setShouldShowIcon(boolean p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/SharedElementCallback.java b/java/ql/test/stubs/google-android-9.0.0/android/app/SharedElementCallback.java new file mode 100644 index 00000000000..1f4cd82b40e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/SharedElementCallback.java @@ -0,0 +1,27 @@ +// Generated automatically from android.app.SharedElementCallback for testing purposes + +package android.app; + +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.RectF; +import android.os.Parcelable; +import android.view.View; +import java.util.List; +import java.util.Map; + +abstract public class SharedElementCallback +{ + public Parcelable onCaptureSharedElementSnapshot(View p0, Matrix p1, RectF p2){ return null; } + public SharedElementCallback(){} + public View onCreateSnapshotView(Context p0, Parcelable p1){ return null; } + public void onMapSharedElements(List p0, Map p1){} + public void onRejectSharedElements(List p0){} + public void onSharedElementEnd(List p0, List p1, List p2){} + public void onSharedElementStart(List p0, List p1, List p2){} + public void onSharedElementsArrived(List p0, List p1, SharedElementCallback.OnSharedElementsReadyListener p2){} + static public interface OnSharedElementsReadyListener + { + void onSharedElementsReady(); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/TaskInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/app/TaskInfo.java new file mode 100644 index 00000000000..a5a38ed25bf --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/TaskInfo.java @@ -0,0 +1,21 @@ +// Generated automatically from android.app.TaskInfo for testing purposes + +package android.app; + +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Intent; + +public class TaskInfo +{ + public ActivityManager.TaskDescription taskDescription = null; + public ComponentName baseActivity = null; + public ComponentName origActivity = null; + public ComponentName topActivity = null; + public Intent baseIntent = null; + public String toString(){ return null; } + public boolean isRunning = false; + public boolean isVisible(){ return false; } + public int numActivities = 0; + public int taskId = 0; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/TaskStackBuilder.java b/java/ql/test/stubs/google-android-9.0.0/android/app/TaskStackBuilder.java new file mode 100644 index 00000000000..39544930481 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/TaskStackBuilder.java @@ -0,0 +1,28 @@ +// Generated automatically from android.app.TaskStackBuilder for testing purposes + +package android.app; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; + +public class TaskStackBuilder +{ + protected TaskStackBuilder() {} + public Intent editIntentAt(int p0){ return null; } + public Intent[] getIntents(){ return null; } + public PendingIntent getPendingIntent(int p0, int p1){ return null; } + public PendingIntent getPendingIntent(int p0, int p1, Bundle p2){ return null; } + public TaskStackBuilder addNextIntent(Intent p0){ return null; } + public TaskStackBuilder addNextIntentWithParentStack(Intent p0){ return null; } + public TaskStackBuilder addParentStack(Activity p0){ return null; } + public TaskStackBuilder addParentStack(Class p0){ return null; } + public TaskStackBuilder addParentStack(ComponentName p0){ return null; } + public int getIntentCount(){ return 0; } + public static TaskStackBuilder create(Context p0){ return null; } + public void startActivities(){} + public void startActivities(Bundle p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/VoiceInteractor.java b/java/ql/test/stubs/google-android-9.0.0/android/app/VoiceInteractor.java new file mode 100644 index 00000000000..5d3f6f654cd --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/VoiceInteractor.java @@ -0,0 +1,31 @@ +// Generated automatically from android.app.VoiceInteractor for testing purposes + +package android.app; + +import android.app.Activity; +import android.content.Context; +import java.util.concurrent.Executor; + +public class VoiceInteractor +{ + abstract static public class Request + { + public Activity getActivity(){ return null; } + public Context getContext(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public void cancel(){} + public void onAttached(Activity p0){} + public void onCancel(){} + public void onDetached(){} + } + public VoiceInteractor.Request getActiveRequest(String p0){ return null; } + public VoiceInteractor.Request[] getActiveRequests(){ return null; } + public boolean isDestroyed(){ return false; } + public boolean registerOnDestroyedCallback(Executor p0, Runnable p1){ return false; } + public boolean submitRequest(VoiceInteractor.Request p0){ return false; } + public boolean submitRequest(VoiceInteractor.Request p0, String p1){ return false; } + public boolean unregisterOnDestroyedCallback(Runnable p0){ return false; } + public boolean[] supportsCommands(String[] p0){ return null; } + public void notifyDirectActionsChanged(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/assist/AssistContent.java b/java/ql/test/stubs/google-android-9.0.0/android/app/assist/AssistContent.java new file mode 100644 index 00000000000..49f515177ea --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/assist/AssistContent.java @@ -0,0 +1,29 @@ +// Generated automatically from android.app.assist.AssistContent for testing purposes + +package android.app.assist; + +import android.content.ClipData; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class AssistContent implements Parcelable +{ + public AssistContent(){} + public Bundle getExtras(){ return null; } + public ClipData getClipData(){ return null; } + public Intent getIntent(){ return null; } + public String getStructuredData(){ return null; } + public Uri getWebUri(){ return null; } + public boolean isAppProvidedIntent(){ return false; } + public boolean isAppProvidedWebUri(){ return false; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void setClipData(ClipData p0){} + public void setIntent(Intent p0){} + public void setStructuredData(String p0){} + public void setWebUri(Uri p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java index d70ac92ec20..f8c83ab104d 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/ComponentCallbacks2.java @@ -2,7 +2,10 @@ package android.content; -public interface ComponentCallbacks2 extends ComponentCallbacks { +import android.content.ComponentCallbacks; + +public interface ComponentCallbacks2 extends ComponentCallbacks +{ static int TRIM_MEMORY_BACKGROUND = 0; static int TRIM_MEMORY_COMPLETE = 0; static int TRIM_MEMORY_MODERATE = 0; @@ -10,6 +13,5 @@ public interface ComponentCallbacks2 extends ComponentCallbacks { static int TRIM_MEMORY_RUNNING_LOW = 0; static int TRIM_MEMORY_RUNNING_MODERATE = 0; static int TRIM_MEMORY_UI_HIDDEN = 0; - void onTrimMemory(int p0); } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/DialogInterface.java b/java/ql/test/stubs/google-android-9.0.0/android/content/DialogInterface.java new file mode 100644 index 00000000000..d47429790f3 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/DialogInterface.java @@ -0,0 +1,41 @@ +// Generated automatically from android.content.DialogInterface for testing purposes + +package android.content; + +import android.view.KeyEvent; + +public interface DialogInterface +{ + static int BUTTON1 = 0; + static int BUTTON2 = 0; + static int BUTTON3 = 0; + static int BUTTON_NEGATIVE = 0; + static int BUTTON_NEUTRAL = 0; + static int BUTTON_POSITIVE = 0; + static public interface OnCancelListener + { + void onCancel(DialogInterface p0); + } + static public interface OnClickListener + { + void onClick(DialogInterface p0, int p1); + } + static public interface OnDismissListener + { + void onDismiss(DialogInterface p0); + } + static public interface OnKeyListener + { + boolean onKey(DialogInterface p0, int p1, KeyEvent p2); + } + static public interface OnMultiChoiceClickListener + { + void onClick(DialogInterface p0, int p1, boolean p2); + } + static public interface OnShowListener + { + void onShow(DialogInterface p0); + } + void cancel(); + void dismiss(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/content/Loader.java b/java/ql/test/stubs/google-android-9.0.0/android/content/Loader.java new file mode 100644 index 00000000000..2b327100cef --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/content/Loader.java @@ -0,0 +1,51 @@ +// Generated automatically from android.content.Loader for testing purposes + +package android.content; + +import android.content.Context; +import java.io.FileDescriptor; +import java.io.PrintWriter; + +public class Loader +{ + protected Loader() {} + protected boolean onCancelLoad(){ return false; } + protected void onAbandon(){} + protected void onForceLoad(){} + protected void onReset(){} + protected void onStartLoading(){} + protected void onStopLoading(){} + public Context getContext(){ return null; } + public Loader(Context p0){} + public String dataToString(D p0){ return null; } + public String toString(){ return null; } + public boolean cancelLoad(){ return false; } + public boolean isAbandoned(){ return false; } + public boolean isReset(){ return false; } + public boolean isStarted(){ return false; } + public boolean takeContentChanged(){ return false; } + public final void startLoading(){} + public int getId(){ return 0; } + public void abandon(){} + public void commitContentChanged(){} + public void deliverCancellation(){} + public void deliverResult(D p0){} + public void dump(String p0, FileDescriptor p1, PrintWriter p2, String[] p3){} + public void forceLoad(){} + public void onContentChanged(){} + public void registerListener(int p0, Loader.OnLoadCompleteListener p1){} + public void registerOnLoadCanceledListener(Loader.OnLoadCanceledListener p0){} + public void reset(){} + public void rollbackContentChanged(){} + public void stopLoading(){} + public void unregisterListener(Loader.OnLoadCompleteListener p0){} + public void unregisterOnLoadCanceledListener(Loader.OnLoadCanceledListener p0){} + static public interface OnLoadCanceledListener + { + void onLoadCanceled(Loader p0); + } + static public interface OnLoadCompleteListener + { + void onLoadComplete(Loader p0, D p1); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/media/AudioAttributes.java b/java/ql/test/stubs/google-android-9.0.0/android/media/AudioAttributes.java index 20dff0cc760..042da801af2 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/media/AudioAttributes.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/media/AudioAttributes.java @@ -11,10 +11,12 @@ public class AudioAttributes implements Parcelable public String toString(){ return null; } public boolean areHapticChannelsMuted(){ return false; } public boolean equals(Object p0){ return false; } + public boolean isContentSpatialized(){ return false; } public int describeContents(){ return 0; } public int getAllowedCapturePolicy(){ return 0; } public int getContentType(){ return 0; } public int getFlags(){ return 0; } + public int getSpatializationBehavior(){ return 0; } public int getUsage(){ return 0; } public int getVolumeControlStream(){ return 0; } public int hashCode(){ return 0; } @@ -30,6 +32,8 @@ public class AudioAttributes implements Parcelable public static int FLAG_AUDIBILITY_ENFORCED = 0; public static int FLAG_HW_AV_SYNC = 0; public static int FLAG_LOW_LATENCY = 0; + public static int SPATIALIZATION_BEHAVIOR_AUTO = 0; + public static int SPATIALIZATION_BEHAVIOR_NEVER = 0; public static int USAGE_ALARM = 0; public static int USAGE_ASSISTANCE_ACCESSIBILITY = 0; public static int USAGE_ASSISTANCE_NAVIGATION_GUIDANCE = 0; diff --git a/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslCertificate.java b/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslCertificate.java new file mode 100644 index 00000000000..8c22fcb0a49 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslCertificate.java @@ -0,0 +1,34 @@ +// Generated automatically from android.net.http.SslCertificate for testing purposes + +package android.net.http; + +import android.os.Bundle; +import java.security.cert.X509Certificate; +import java.util.Date; + +public class SslCertificate +{ + protected SslCertificate() {} + public Date getValidNotAfterDate(){ return null; } + public Date getValidNotBeforeDate(){ return null; } + public SslCertificate(String p0, String p1, Date p2, Date p3){} + public SslCertificate(String p0, String p1, String p2, String p3){} + public SslCertificate(X509Certificate p0){} + public SslCertificate.DName getIssuedBy(){ return null; } + public SslCertificate.DName getIssuedTo(){ return null; } + public String getValidNotAfter(){ return null; } + public String getValidNotBefore(){ return null; } + public String toString(){ return null; } + public X509Certificate getX509Certificate(){ return null; } + public class DName + { + protected DName() {} + public DName(String p0){} + public String getCName(){ return null; } + public String getDName(){ return null; } + public String getOName(){ return null; } + public String getUName(){ return null; } + } + public static Bundle saveState(SslCertificate p0){ return null; } + public static SslCertificate restoreState(Bundle p0){ return null; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslError.java b/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslError.java new file mode 100644 index 00000000000..62feb4a498d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/net/http/SslError.java @@ -0,0 +1,28 @@ +// Generated automatically from android.net.http.SslError for testing purposes + +package android.net.http; + +import android.net.http.SslCertificate; +import java.security.cert.X509Certificate; + +public class SslError +{ + protected SslError() {} + public SslCertificate getCertificate(){ return null; } + public SslError(int p0, SslCertificate p1){} + public SslError(int p0, SslCertificate p1, String p2){} + public SslError(int p0, X509Certificate p1){} + public SslError(int p0, X509Certificate p1, String p2){} + public String getUrl(){ return null; } + public String toString(){ return null; } + public boolean addError(int p0){ return false; } + public boolean hasError(int p0){ return false; } + public int getPrimaryError(){ return 0; } + public static int SSL_DATE_INVALID = 0; + public static int SSL_EXPIRED = 0; + public static int SSL_IDMISMATCH = 0; + public static int SSL_INVALID = 0; + public static int SSL_MAX_ERROR = 0; + public static int SSL_NOTYETVALID = 0; + public static int SSL_UNTRUSTED = 0; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Debug.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Debug.java new file mode 100644 index 00000000000..2ce002f144c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Debug.java @@ -0,0 +1,110 @@ +// Generated automatically from android.os.Debug for testing purposes + +package android.os; + +import android.os.Parcel; +import android.os.Parcelable; +import java.io.FileDescriptor; +import java.util.Map; + +public class Debug +{ + protected Debug() {} + public static Map getRuntimeStats(){ return null; } + public static String getRuntimeStat(String p0){ return null; } + public static boolean dumpService(String p0, FileDescriptor p1, String[] p2){ return false; } + public static boolean isDebuggerConnected(){ return false; } + public static boolean waitingForDebugger(){ return false; } + public static int SHOW_CLASSLOADER = 0; + public static int SHOW_FULL_DETAIL = 0; + public static int SHOW_INITIALIZED = 0; + public static int TRACE_COUNT_ALLOCS = 0; + public static int getBinderDeathObjectCount(){ return 0; } + public static int getBinderLocalObjectCount(){ return 0; } + public static int getBinderProxyObjectCount(){ return 0; } + public static int getBinderReceivedTransactions(){ return 0; } + public static int getBinderSentTransactions(){ return 0; } + public static int getGlobalAllocCount(){ return 0; } + public static int getGlobalAllocSize(){ return 0; } + public static int getGlobalClassInitCount(){ return 0; } + public static int getGlobalClassInitTime(){ return 0; } + public static int getGlobalExternalAllocCount(){ return 0; } + public static int getGlobalExternalAllocSize(){ return 0; } + public static int getGlobalExternalFreedCount(){ return 0; } + public static int getGlobalExternalFreedSize(){ return 0; } + public static int getGlobalFreedCount(){ return 0; } + public static int getGlobalFreedSize(){ return 0; } + public static int getGlobalGcInvocationCount(){ return 0; } + public static int getLoadedClassCount(){ return 0; } + public static int getThreadAllocCount(){ return 0; } + public static int getThreadAllocSize(){ return 0; } + public static int getThreadExternalAllocCount(){ return 0; } + public static int getThreadExternalAllocSize(){ return 0; } + public static int getThreadGcInvocationCount(){ return 0; } + public static int setAllocationLimit(int p0){ return 0; } + public static int setGlobalAllocationLimit(int p0){ return 0; } + public static long getNativeHeapAllocatedSize(){ return 0; } + public static long getNativeHeapFreeSize(){ return 0; } + public static long getNativeHeapSize(){ return 0; } + public static long getPss(){ return 0; } + public static long threadCpuTimeNanos(){ return 0; } + public static void attachJvmtiAgent(String p0, String p1, ClassLoader p2){} + public static void changeDebugPort(int p0){} + public static void dumpHprofData(String p0){} + public static void enableEmulatorTraceOutput(){} + public static void getMemoryInfo(Debug.MemoryInfo p0){} + public static void printLoadedClasses(int p0){} + public static void resetAllCounts(){} + public static void resetGlobalAllocCount(){} + public static void resetGlobalAllocSize(){} + public static void resetGlobalClassInitCount(){} + public static void resetGlobalClassInitTime(){} + public static void resetGlobalExternalAllocCount(){} + public static void resetGlobalExternalAllocSize(){} + public static void resetGlobalExternalFreedCount(){} + public static void resetGlobalExternalFreedSize(){} + public static void resetGlobalFreedCount(){} + public static void resetGlobalFreedSize(){} + public static void resetGlobalGcInvocationCount(){} + public static void resetThreadAllocCount(){} + public static void resetThreadAllocSize(){} + public static void resetThreadExternalAllocCount(){} + public static void resetThreadExternalAllocSize(){} + public static void resetThreadGcInvocationCount(){} + public static void startAllocCounting(){} + public static void startMethodTracing(){} + public static void startMethodTracing(String p0){} + public static void startMethodTracing(String p0, int p1){} + public static void startMethodTracing(String p0, int p1, int p2){} + public static void startMethodTracingSampling(String p0, int p1, int p2){} + public static void startNativeTracing(){} + public static void stopAllocCounting(){} + public static void stopMethodTracing(){} + public static void stopNativeTracing(){} + public static void waitForDebugger(){} + static public class MemoryInfo implements Parcelable + { + public Map getMemoryStats(){ return null; } + public MemoryInfo(){} + public String getMemoryStat(String p0){ return null; } + public int dalvikPrivateDirty = 0; + public int dalvikPss = 0; + public int dalvikSharedDirty = 0; + public int describeContents(){ return 0; } + public int getTotalPrivateClean(){ return 0; } + public int getTotalPrivateDirty(){ return 0; } + public int getTotalPss(){ return 0; } + public int getTotalSharedClean(){ return 0; } + public int getTotalSharedDirty(){ return 0; } + public int getTotalSwappablePss(){ return 0; } + public int nativePrivateDirty = 0; + public int nativePss = 0; + public int nativeSharedDirty = 0; + public int otherPrivateDirty = 0; + public int otherPss = 0; + public int otherSharedDirty = 0; + public static Parcelable.Creator CREATOR = null; + public void readFromParcel(Parcel p0){} + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java index 626061a6799..3aceab4de0a 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/os/Parcelable.java @@ -2,6 +2,7 @@ package android.os; +import android.app.Fragment; import android.os.Parcel; public interface Parcelable @@ -9,6 +10,10 @@ public interface Parcelable int describeContents(); static int CONTENTS_FILE_DESCRIPTOR = 0; static int PARCELABLE_WRITE_RETURN_VALUE = 0; + static public interface ClassLoaderCreator extends Parcelable.Creator + { + T createFromParcel(Parcel p0, ClassLoader p1); + } static public interface Creator { T createFromParcel(Parcel p0); diff --git a/java/ql/test/stubs/google-android-9.0.0/android/print/PageRange.java b/java/ql/test/stubs/google-android-9.0.0/android/print/PageRange.java new file mode 100644 index 00000000000..7e8a182c81d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/print/PageRange.java @@ -0,0 +1,21 @@ +// Generated automatically from android.print.PageRange for testing purposes + +package android.print; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PageRange implements Parcelable +{ + protected PageRange() {} + public PageRange(int p0, int p1){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getEnd(){ return 0; } + public int getStart(){ return 0; } + public int hashCode(){ return 0; } + public static PageRange ALL_PAGES = null; + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/print/PrintAttributes.java b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintAttributes.java new file mode 100644 index 00000000000..82578cf9f76 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintAttributes.java @@ -0,0 +1,162 @@ +// Generated automatically from android.print.PrintAttributes for testing purposes + +package android.print; + +import android.content.pm.PackageManager; +import android.os.Parcel; +import android.os.Parcelable; + +public class PrintAttributes implements Parcelable +{ + public PrintAttributes.Margins getMinMargins(){ return null; } + public PrintAttributes.MediaSize getMediaSize(){ return null; } + public PrintAttributes.Resolution getResolution(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getColorMode(){ return 0; } + public int getDuplexMode(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int COLOR_MODE_COLOR = 0; + public static int COLOR_MODE_MONOCHROME = 0; + public static int DUPLEX_MODE_LONG_EDGE = 0; + public static int DUPLEX_MODE_NONE = 0; + public static int DUPLEX_MODE_SHORT_EDGE = 0; + public void writeToParcel(Parcel p0, int p1){} + static public class Margins + { + protected Margins() {} + public Margins(int p0, int p1, int p2, int p3){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int getBottomMils(){ return 0; } + public int getLeftMils(){ return 0; } + public int getRightMils(){ return 0; } + public int getTopMils(){ return 0; } + public int hashCode(){ return 0; } + public static PrintAttributes.Margins NO_MARGINS = null; + } + static public class MediaSize + { + protected MediaSize() {} + public MediaSize(String p0, String p1, int p2, int p3){} + public PrintAttributes.MediaSize asLandscape(){ return null; } + public PrintAttributes.MediaSize asPortrait(){ return null; } + public String getId(){ return null; } + public String getLabel(PackageManager p0){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean isPortrait(){ return false; } + public int getHeightMils(){ return 0; } + public int getWidthMils(){ return 0; } + public int hashCode(){ return 0; } + public static PrintAttributes.MediaSize ANSI_C = null; + public static PrintAttributes.MediaSize ANSI_D = null; + public static PrintAttributes.MediaSize ANSI_E = null; + public static PrintAttributes.MediaSize ANSI_F = null; + public static PrintAttributes.MediaSize ISO_A0 = null; + public static PrintAttributes.MediaSize ISO_A1 = null; + public static PrintAttributes.MediaSize ISO_A10 = null; + public static PrintAttributes.MediaSize ISO_A2 = null; + public static PrintAttributes.MediaSize ISO_A3 = null; + public static PrintAttributes.MediaSize ISO_A4 = null; + public static PrintAttributes.MediaSize ISO_A5 = null; + public static PrintAttributes.MediaSize ISO_A6 = null; + public static PrintAttributes.MediaSize ISO_A7 = null; + public static PrintAttributes.MediaSize ISO_A8 = null; + public static PrintAttributes.MediaSize ISO_A9 = null; + public static PrintAttributes.MediaSize ISO_B0 = null; + public static PrintAttributes.MediaSize ISO_B1 = null; + public static PrintAttributes.MediaSize ISO_B10 = null; + public static PrintAttributes.MediaSize ISO_B2 = null; + public static PrintAttributes.MediaSize ISO_B3 = null; + public static PrintAttributes.MediaSize ISO_B4 = null; + public static PrintAttributes.MediaSize ISO_B5 = null; + public static PrintAttributes.MediaSize ISO_B6 = null; + public static PrintAttributes.MediaSize ISO_B7 = null; + public static PrintAttributes.MediaSize ISO_B8 = null; + public static PrintAttributes.MediaSize ISO_B9 = null; + public static PrintAttributes.MediaSize ISO_C0 = null; + public static PrintAttributes.MediaSize ISO_C1 = null; + public static PrintAttributes.MediaSize ISO_C10 = null; + public static PrintAttributes.MediaSize ISO_C2 = null; + public static PrintAttributes.MediaSize ISO_C3 = null; + public static PrintAttributes.MediaSize ISO_C4 = null; + public static PrintAttributes.MediaSize ISO_C5 = null; + public static PrintAttributes.MediaSize ISO_C6 = null; + public static PrintAttributes.MediaSize ISO_C7 = null; + public static PrintAttributes.MediaSize ISO_C8 = null; + public static PrintAttributes.MediaSize ISO_C9 = null; + public static PrintAttributes.MediaSize JIS_B0 = null; + public static PrintAttributes.MediaSize JIS_B1 = null; + public static PrintAttributes.MediaSize JIS_B10 = null; + public static PrintAttributes.MediaSize JIS_B2 = null; + public static PrintAttributes.MediaSize JIS_B3 = null; + public static PrintAttributes.MediaSize JIS_B4 = null; + public static PrintAttributes.MediaSize JIS_B5 = null; + public static PrintAttributes.MediaSize JIS_B6 = null; + public static PrintAttributes.MediaSize JIS_B7 = null; + public static PrintAttributes.MediaSize JIS_B8 = null; + public static PrintAttributes.MediaSize JIS_B9 = null; + public static PrintAttributes.MediaSize JIS_EXEC = null; + public static PrintAttributes.MediaSize JPN_CHOU2 = null; + public static PrintAttributes.MediaSize JPN_CHOU3 = null; + public static PrintAttributes.MediaSize JPN_CHOU4 = null; + public static PrintAttributes.MediaSize JPN_HAGAKI = null; + public static PrintAttributes.MediaSize JPN_KAHU = null; + public static PrintAttributes.MediaSize JPN_KAKU2 = null; + public static PrintAttributes.MediaSize JPN_OE_PHOTO_L = null; + public static PrintAttributes.MediaSize JPN_OUFUKU = null; + public static PrintAttributes.MediaSize JPN_YOU4 = null; + public static PrintAttributes.MediaSize NA_ARCH_A = null; + public static PrintAttributes.MediaSize NA_ARCH_B = null; + public static PrintAttributes.MediaSize NA_ARCH_C = null; + public static PrintAttributes.MediaSize NA_ARCH_D = null; + public static PrintAttributes.MediaSize NA_ARCH_E = null; + public static PrintAttributes.MediaSize NA_ARCH_E1 = null; + public static PrintAttributes.MediaSize NA_FOOLSCAP = null; + public static PrintAttributes.MediaSize NA_GOVT_LETTER = null; + public static PrintAttributes.MediaSize NA_INDEX_3X5 = null; + public static PrintAttributes.MediaSize NA_INDEX_4X6 = null; + public static PrintAttributes.MediaSize NA_INDEX_5X8 = null; + public static PrintAttributes.MediaSize NA_JUNIOR_LEGAL = null; + public static PrintAttributes.MediaSize NA_LEDGER = null; + public static PrintAttributes.MediaSize NA_LEGAL = null; + public static PrintAttributes.MediaSize NA_LETTER = null; + public static PrintAttributes.MediaSize NA_MONARCH = null; + public static PrintAttributes.MediaSize NA_QUARTO = null; + public static PrintAttributes.MediaSize NA_SUPER_B = null; + public static PrintAttributes.MediaSize NA_TABLOID = null; + public static PrintAttributes.MediaSize OM_DAI_PA_KAI = null; + public static PrintAttributes.MediaSize OM_JUURO_KU_KAI = null; + public static PrintAttributes.MediaSize OM_PA_KAI = null; + public static PrintAttributes.MediaSize PRC_1 = null; + public static PrintAttributes.MediaSize PRC_10 = null; + public static PrintAttributes.MediaSize PRC_16K = null; + public static PrintAttributes.MediaSize PRC_2 = null; + public static PrintAttributes.MediaSize PRC_3 = null; + public static PrintAttributes.MediaSize PRC_4 = null; + public static PrintAttributes.MediaSize PRC_5 = null; + public static PrintAttributes.MediaSize PRC_6 = null; + public static PrintAttributes.MediaSize PRC_7 = null; + public static PrintAttributes.MediaSize PRC_8 = null; + public static PrintAttributes.MediaSize PRC_9 = null; + public static PrintAttributes.MediaSize ROC_16K = null; + public static PrintAttributes.MediaSize ROC_8K = null; + public static PrintAttributes.MediaSize UNKNOWN_LANDSCAPE = null; + public static PrintAttributes.MediaSize UNKNOWN_PORTRAIT = null; + } + static public class Resolution + { + protected Resolution() {} + public Resolution(String p0, String p1, int p2, int p3){} + public String getId(){ return null; } + public String getLabel(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int getHorizontalDpi(){ return 0; } + public int getVerticalDpi(){ return 0; } + public int hashCode(){ return 0; } + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentAdapter.java b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentAdapter.java new file mode 100644 index 00000000000..bee84a4361f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentAdapter.java @@ -0,0 +1,32 @@ +// Generated automatically from android.print.PrintDocumentAdapter for testing purposes + +package android.print; + +import android.os.Bundle; +import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; +import android.print.PageRange; +import android.print.PrintAttributes; +import android.print.PrintDocumentInfo; + +abstract public class PrintDocumentAdapter +{ + abstract static public class LayoutResultCallback + { + public void onLayoutCancelled(){} + public void onLayoutFailed(CharSequence p0){} + public void onLayoutFinished(PrintDocumentInfo p0, boolean p1){} + } + abstract static public class WriteResultCallback + { + public void onWriteCancelled(){} + public void onWriteFailed(CharSequence p0){} + public void onWriteFinished(PageRange[] p0){} + } + public PrintDocumentAdapter(){} + public abstract void onLayout(PrintAttributes p0, PrintAttributes p1, CancellationSignal p2, PrintDocumentAdapter.LayoutResultCallback p3, Bundle p4); + public abstract void onWrite(PageRange[] p0, ParcelFileDescriptor p1, CancellationSignal p2, PrintDocumentAdapter.WriteResultCallback p3); + public static String EXTRA_PRINT_PREVIEW = null; + public void onFinish(){} + public void onStart(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentInfo.java new file mode 100644 index 00000000000..bc42c86d4f2 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/print/PrintDocumentInfo.java @@ -0,0 +1,25 @@ +// Generated automatically from android.print.PrintDocumentInfo for testing purposes + +package android.print; + +import android.os.Parcel; +import android.os.Parcelable; + +public class PrintDocumentInfo implements Parcelable +{ + protected PrintDocumentInfo() {} + public String getName(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getContentType(){ return 0; } + public int getPageCount(){ return 0; } + public int hashCode(){ return 0; } + public long getDataSize(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int CONTENT_TYPE_DOCUMENT = 0; + public static int CONTENT_TYPE_PHOTO = 0; + public static int CONTENT_TYPE_UNKNOWN = 0; + public static int PAGE_COUNT_UNKNOWN = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/text/Spannable.java b/java/ql/test/stubs/google-android-9.0.0/android/text/Spannable.java index eb55f4778ef..f615e7328a5 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/text/Spannable.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/text/Spannable.java @@ -6,6 +6,12 @@ import android.text.Spanned; public interface Spannable extends Spanned { + static public class Factory + { + public Factory(){} + public Spannable newSpannable(CharSequence p0){ return null; } + public static Spannable.Factory getInstance(){ return null; } + } void removeSpan(Object p0); void setSpan(Object p0, int p1, int p2, int p3); } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/PathMotion.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/PathMotion.java new file mode 100644 index 00000000000..91b7a1cf016 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/PathMotion.java @@ -0,0 +1,14 @@ +// Generated automatically from android.transition.PathMotion for testing purposes + +package android.transition; + +import android.content.Context; +import android.graphics.Path; +import android.util.AttributeSet; + +abstract public class PathMotion +{ + public PathMotion(){} + public PathMotion(Context p0, AttributeSet p1){} + public abstract Path getPath(float p0, float p1, float p2, float p3); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/Scene.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/Scene.java new file mode 100644 index 00000000000..57fe7cc104e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/Scene.java @@ -0,0 +1,22 @@ +// Generated automatically from android.transition.Scene for testing purposes + +package android.transition; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; + +public class Scene +{ + protected Scene() {} + public Scene(ViewGroup p0){} + public Scene(ViewGroup p0, View p1){} + public Scene(ViewGroup p0, ViewGroup p1){} + public ViewGroup getSceneRoot(){ return null; } + public static Scene getCurrentScene(ViewGroup p0){ return null; } + public static Scene getSceneForLayout(ViewGroup p0, int p1, Context p2){ return null; } + public void enter(){} + public void exit(){} + public void setEnterAction(Runnable p0){} + public void setExitAction(Runnable p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/Transition.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/Transition.java new file mode 100644 index 00000000000..8adeae42c36 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/Transition.java @@ -0,0 +1,83 @@ +// Generated automatically from android.transition.Transition for testing purposes + +package android.transition; + +import android.animation.Animator; +import android.animation.TimeInterpolator; +import android.content.Context; +import android.graphics.Rect; +import android.transition.PathMotion; +import android.transition.TransitionPropagation; +import android.transition.TransitionValues; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import java.util.List; + +abstract public class Transition implements Cloneable +{ + abstract static public class EpicenterCallback + { + public EpicenterCallback(){} + public abstract Rect onGetEpicenter(Transition p0); + } + public Animator createAnimator(ViewGroup p0, TransitionValues p1, TransitionValues p2){ return null; } + public List getTargetTypes(){ return null; } + public List getTargetIds(){ return null; } + public List getTargetNames(){ return null; } + public List getTargets(){ return null; } + public PathMotion getPathMotion(){ return null; } + public Rect getEpicenter(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public String[] getTransitionProperties(){ return null; } + public TimeInterpolator getInterpolator(){ return null; } + public Transition addListener(Transition.TransitionListener p0){ return null; } + public Transition addTarget(Class p0){ return null; } + public Transition addTarget(String p0){ return null; } + public Transition addTarget(View p0){ return null; } + public Transition addTarget(int p0){ return null; } + public Transition clone(){ return null; } + public Transition excludeChildren(Class p0, boolean p1){ return null; } + public Transition excludeChildren(View p0, boolean p1){ return null; } + public Transition excludeChildren(int p0, boolean p1){ return null; } + public Transition excludeTarget(Class p0, boolean p1){ return null; } + public Transition excludeTarget(String p0, boolean p1){ return null; } + public Transition excludeTarget(View p0, boolean p1){ return null; } + public Transition excludeTarget(int p0, boolean p1){ return null; } + public Transition removeListener(Transition.TransitionListener p0){ return null; } + public Transition removeTarget(Class p0){ return null; } + public Transition removeTarget(String p0){ return null; } + public Transition removeTarget(View p0){ return null; } + public Transition removeTarget(int p0){ return null; } + public Transition setDuration(long p0){ return null; } + public Transition setInterpolator(TimeInterpolator p0){ return null; } + public Transition setStartDelay(long p0){ return null; } + public Transition(){} + public Transition(Context p0, AttributeSet p1){} + public Transition.EpicenterCallback getEpicenterCallback(){ return null; } + public TransitionPropagation getPropagation(){ return null; } + public TransitionValues getTransitionValues(View p0, boolean p1){ return null; } + public abstract void captureEndValues(TransitionValues p0); + public abstract void captureStartValues(TransitionValues p0); + public boolean canRemoveViews(){ return false; } + public boolean isTransitionRequired(TransitionValues p0, TransitionValues p1){ return false; } + public long getDuration(){ return 0; } + public long getStartDelay(){ return 0; } + public static int MATCH_ID = 0; + public static int MATCH_INSTANCE = 0; + public static int MATCH_ITEM_ID = 0; + public static int MATCH_NAME = 0; + public void setEpicenterCallback(Transition.EpicenterCallback p0){} + public void setMatchOrder(int... p0){} + public void setPathMotion(PathMotion p0){} + public void setPropagation(TransitionPropagation p0){} + static public interface TransitionListener + { + void onTransitionCancel(Transition p0); + void onTransitionEnd(Transition p0); + void onTransitionPause(Transition p0); + void onTransitionResume(Transition p0); + void onTransitionStart(Transition p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionManager.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionManager.java new file mode 100644 index 00000000000..79a91ccabba --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionManager.java @@ -0,0 +1,20 @@ +// Generated automatically from android.transition.TransitionManager for testing purposes + +package android.transition; + +import android.transition.Scene; +import android.transition.Transition; +import android.view.ViewGroup; + +public class TransitionManager +{ + public TransitionManager(){} + public static void beginDelayedTransition(ViewGroup p0){} + public static void beginDelayedTransition(ViewGroup p0, Transition p1){} + public static void endTransitions(ViewGroup p0){} + public static void go(Scene p0){} + public static void go(Scene p0, Transition p1){} + public void setTransition(Scene p0, Scene p1, Transition p2){} + public void setTransition(Scene p0, Transition p1){} + public void transitionTo(Scene p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionPropagation.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionPropagation.java new file mode 100644 index 00000000000..0101e4f5cd2 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionPropagation.java @@ -0,0 +1,15 @@ +// Generated automatically from android.transition.TransitionPropagation for testing purposes + +package android.transition; + +import android.transition.Transition; +import android.transition.TransitionValues; +import android.view.ViewGroup; + +abstract public class TransitionPropagation +{ + public TransitionPropagation(){} + public abstract String[] getPropagationProperties(); + public abstract long getStartDelay(ViewGroup p0, Transition p1, TransitionValues p2, TransitionValues p3); + public abstract void captureValues(TransitionValues p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionValues.java b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionValues.java new file mode 100644 index 00000000000..27cbfe690f5 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/transition/TransitionValues.java @@ -0,0 +1,17 @@ +// Generated automatically from android.transition.TransitionValues for testing purposes + +package android.transition; + +import android.view.View; +import java.util.Map; + +public class TransitionValues +{ + public String toString(){ return null; } + public TransitionValues(){} + public TransitionValues(View p0){} + public View view = null; + public boolean equals(Object p0){ return false; } + public final Map values = null; + public int hashCode(){ return 0; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/AttachedSurfaceControl.java b/java/ql/test/stubs/google-android-9.0.0/android/view/AttachedSurfaceControl.java index 7ac18735ee3..e4140572b6a 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/AttachedSurfaceControl.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/AttachedSurfaceControl.java @@ -8,4 +8,11 @@ public interface AttachedSurfaceControl { SurfaceControl.Transaction buildReparentTransaction(SurfaceControl p0); boolean applyTransactionOnDraw(SurfaceControl.Transaction p0); + default int getBufferTransformHint(){ return 0; } + default void addOnBufferTransformHintChangedListener(AttachedSurfaceControl.OnBufferTransformHintChangedListener p0){} + default void removeOnBufferTransformHintChangedListener(AttachedSurfaceControl.OnBufferTransformHintChangedListener p0){} + static public interface OnBufferTransformHintChangedListener + { + void onBufferTransformHintChanged(int p0); + } } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/DragAndDropPermissions.java b/java/ql/test/stubs/google-android-9.0.0/android/view/DragAndDropPermissions.java new file mode 100644 index 00000000000..c0b47133d07 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/DragAndDropPermissions.java @@ -0,0 +1,15 @@ +// Generated automatically from android.view.DragAndDropPermissions for testing purposes + +package android.view; + +import android.os.Parcel; +import android.os.Parcelable; + +public class DragAndDropPermissions implements Parcelable +{ + protected DragAndDropPermissions() {} + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void release(){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/FrameMetrics.java b/java/ql/test/stubs/google-android-9.0.0/android/view/FrameMetrics.java new file mode 100644 index 00000000000..98c91865a80 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/FrameMetrics.java @@ -0,0 +1,25 @@ +// Generated automatically from android.view.FrameMetrics for testing purposes + +package android.view; + + +public class FrameMetrics +{ + protected FrameMetrics() {} + public FrameMetrics(FrameMetrics p0){} + public long getMetric(int p0){ return 0; } + public static int ANIMATION_DURATION = 0; + public static int COMMAND_ISSUE_DURATION = 0; + public static int DEADLINE = 0; + public static int DRAW_DURATION = 0; + public static int FIRST_DRAW_FRAME = 0; + public static int GPU_DURATION = 0; + public static int INPUT_HANDLING_DURATION = 0; + public static int INTENDED_VSYNC_TIMESTAMP = 0; + public static int LAYOUT_MEASURE_DURATION = 0; + public static int SWAP_BUFFERS_DURATION = 0; + public static int SYNC_DURATION = 0; + public static int TOTAL_DURATION = 0; + public static int UNKNOWN_DELAY_DURATION = 0; + public static int VSYNC_TIMESTAMP = 0; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/InputQueue.java b/java/ql/test/stubs/google-android-9.0.0/android/view/InputQueue.java new file mode 100644 index 00000000000..083a7bdb4b1 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/InputQueue.java @@ -0,0 +1,14 @@ +// Generated automatically from android.view.InputQueue for testing purposes + +package android.view; + + +public class InputQueue +{ + protected void finalize(){} + static public interface Callback + { + void onInputQueueCreated(InputQueue p0); + void onInputQueueDestroyed(InputQueue p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutGroup.java b/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutGroup.java new file mode 100644 index 00000000000..13f1cdaddc2 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutGroup.java @@ -0,0 +1,21 @@ +// Generated automatically from android.view.KeyboardShortcutGroup for testing purposes + +package android.view; + +import android.os.Parcel; +import android.os.Parcelable; +import android.view.KeyboardShortcutInfo; +import java.util.List; + +public class KeyboardShortcutGroup implements Parcelable +{ + protected KeyboardShortcutGroup() {} + public CharSequence getLabel(){ return null; } + public KeyboardShortcutGroup(CharSequence p0){} + public KeyboardShortcutGroup(CharSequence p0, List p1){} + public List getItems(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void addItem(KeyboardShortcutInfo p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutInfo.java new file mode 100644 index 00000000000..a6d682ce01f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/KeyboardShortcutInfo.java @@ -0,0 +1,20 @@ +// Generated automatically from android.view.KeyboardShortcutInfo for testing purposes + +package android.view; + +import android.os.Parcel; +import android.os.Parcelable; + +public class KeyboardShortcutInfo implements Parcelable +{ + protected KeyboardShortcutInfo() {} + public CharSequence getLabel(){ return null; } + public KeyboardShortcutInfo(CharSequence p0, char p1, int p2){} + public KeyboardShortcutInfo(CharSequence p0, int p1, int p2){} + public char getBaseCharacter(){ return '0'; } + public int describeContents(){ return 0; } + public int getKeycode(){ return 0; } + public int getModifiers(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/SearchEvent.java b/java/ql/test/stubs/google-android-9.0.0/android/view/SearchEvent.java new file mode 100644 index 00000000000..064261513cf --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/SearchEvent.java @@ -0,0 +1,12 @@ +// Generated automatically from android.view.SearchEvent for testing purposes + +package android.view; + +import android.view.InputDevice; + +public class SearchEvent +{ + protected SearchEvent() {} + public InputDevice getInputDevice(){ return null; } + public SearchEvent(InputDevice p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceControl.java b/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceControl.java index 6feff4a32c2..8ea03128948 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceControl.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceControl.java @@ -14,6 +14,12 @@ public class SurfaceControl implements Parcelable public boolean isValid(){ return false; } public int describeContents(){ return 0; } public static Parcelable.Creator CREATOR = null; + public static int BUFFER_TRANSFORM_IDENTITY = 0; + public static int BUFFER_TRANSFORM_MIRROR_HORIZONTAL = 0; + public static int BUFFER_TRANSFORM_MIRROR_VERTICAL = 0; + public static int BUFFER_TRANSFORM_ROTATE_180 = 0; + public static int BUFFER_TRANSFORM_ROTATE_270 = 0; + public static int BUFFER_TRANSFORM_ROTATE_90 = 0; public void readFromParcel(Parcel p0){} public void release(){} public void writeToParcel(Parcel p0, int p1){} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceHolder.java b/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceHolder.java new file mode 100644 index 00000000000..561f640b706 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/SurfaceHolder.java @@ -0,0 +1,40 @@ +// Generated automatically from android.view.SurfaceHolder for testing purposes + +package android.view; + +import android.graphics.Canvas; +import android.graphics.Rect; +import android.view.Surface; + +public interface SurfaceHolder +{ + Canvas lockCanvas(); + Canvas lockCanvas(Rect p0); + Rect getSurfaceFrame(); + Surface getSurface(); + boolean isCreating(); + default Canvas lockHardwareCanvas(){ return null; } + static int SURFACE_TYPE_GPU = 0; + static int SURFACE_TYPE_HARDWARE = 0; + static int SURFACE_TYPE_NORMAL = 0; + static int SURFACE_TYPE_PUSH_BUFFERS = 0; + static public interface Callback + { + void surfaceChanged(SurfaceHolder p0, int p1, int p2, int p3); + void surfaceCreated(SurfaceHolder p0); + void surfaceDestroyed(SurfaceHolder p0); + } + static public interface Callback2 extends SurfaceHolder.Callback + { + default void surfaceRedrawNeededAsync(SurfaceHolder p0, Runnable p1){} + void surfaceRedrawNeeded(SurfaceHolder p0); + } + void addCallback(SurfaceHolder.Callback p0); + void removeCallback(SurfaceHolder.Callback p0); + void setFixedSize(int p0, int p1); + void setFormat(int p0); + void setKeepScreenOn(boolean p0); + void setSizeFromLayout(); + void setType(int p0); + void unlockCanvasAndPost(Canvas p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/View.java b/java/ql/test/stubs/google-android-9.0.0/android/view/View.java index 2004c624c6e..e91fa287eb2 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/View.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/View.java @@ -525,6 +525,7 @@ public class View implements AccessibilityEventSource, Drawable.Callback, KeyEve public static int AUTOFILL_TYPE_NONE = 0; public static int AUTOFILL_TYPE_TEXT = 0; public static int AUTOFILL_TYPE_TOGGLE = 0; + public static int DRAG_FLAG_ACCESSIBILITY_ACTION = 0; public static int DRAG_FLAG_GLOBAL = 0; public static int DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION = 0; public static int DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION = 0; diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/ViewGroup.java b/java/ql/test/stubs/google-android-9.0.0/android/view/ViewGroup.java index 7e05f4b3781..a90504337ff 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/ViewGroup.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/ViewGroup.java @@ -258,6 +258,27 @@ abstract public class ViewGroup extends View implements ViewManager, ViewParent public static int WRAP_CONTENT = 0; public void resolveLayoutDirection(int p0){} } + static public class MarginLayoutParams extends ViewGroup.LayoutParams + { + protected MarginLayoutParams() {} + public MarginLayoutParams(Context p0, AttributeSet p1){} + public MarginLayoutParams(ViewGroup.LayoutParams p0){} + public MarginLayoutParams(ViewGroup.MarginLayoutParams p0){} + public MarginLayoutParams(int p0, int p1){} + public boolean isMarginRelative(){ return false; } + public int bottomMargin = 0; + public int getLayoutDirection(){ return 0; } + public int getMarginEnd(){ return 0; } + public int getMarginStart(){ return 0; } + public int leftMargin = 0; + public int rightMargin = 0; + public int topMargin = 0; + public void resolveLayoutDirection(int p0){} + public void setLayoutDirection(int p0){} + public void setMarginEnd(int p0){} + public void setMarginStart(int p0){} + public void setMargins(int p0, int p1, int p2, int p3){} + } static public interface OnHierarchyChangeListener { void onChildViewAdded(View p0, View p1); diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/Window.java b/java/ql/test/stubs/google-android-9.0.0/android/view/Window.java new file mode 100644 index 00000000000..13f5e79ca4a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/Window.java @@ -0,0 +1,251 @@ +// Generated automatically from android.view.Window for testing purposes + +package android.view; + +import android.content.Context; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.media.session.MediaController; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.transition.Scene; +import android.transition.Transition; +import android.transition.TransitionManager; +import android.view.ActionMode; +import android.view.AttachedSurfaceControl; +import android.view.FrameMetrics; +import android.view.InputEvent; +import android.view.InputQueue; +import android.view.KeyEvent; +import android.view.KeyboardShortcutGroup; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.ScrollCaptureCallback; +import android.view.SearchEvent; +import android.view.SurfaceHolder; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsetsController; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityEvent; +import java.util.List; + +abstract public class Window +{ + protected Window() {} + protected abstract void onActive(); + protected final boolean hasSoftInputMode(){ return false; } + protected final int getFeatures(){ return 0; } + protected final int getForcedWindowFlags(){ return 0; } + protected final int getLocalFeatures(){ return 0; } + protected static int DEFAULT_FEATURES = 0; + protected void setDefaultWindowFormat(int p0){} + public T findViewById(int p0){ return null; } + public AttachedSurfaceControl getRootSurfaceControl(){ return null; } + public List getSystemGestureExclusionRects(){ return null; } + public MediaController getMediaController(){ return null; } + public Scene getContentScene(){ return null; } + public Transition getEnterTransition(){ return null; } + public Transition getExitTransition(){ return null; } + public Transition getReenterTransition(){ return null; } + public Transition getReturnTransition(){ return null; } + public Transition getSharedElementEnterTransition(){ return null; } + public Transition getSharedElementExitTransition(){ return null; } + public Transition getSharedElementReenterTransition(){ return null; } + public Transition getSharedElementReturnTransition(){ return null; } + public TransitionManager getTransitionManager(){ return null; } + public Window(Context p0){} + public WindowInsetsController getInsetsController(){ return null; } + public WindowManager getWindowManager(){ return null; } + public abstract Bundle saveHierarchyState(); + public abstract LayoutInflater getLayoutInflater(); + public abstract View getCurrentFocus(); + public abstract View getDecorView(); + public abstract View peekDecorView(); + public abstract boolean isFloating(); + public abstract boolean isShortcutKey(int p0, KeyEvent p1); + public abstract boolean performContextMenuIdentifierAction(int p0, int p1); + public abstract boolean performPanelIdentifierAction(int p0, int p1, int p2); + public abstract boolean performPanelShortcut(int p0, int p1, KeyEvent p2, int p3); + public abstract boolean superDispatchGenericMotionEvent(MotionEvent p0); + public abstract boolean superDispatchKeyEvent(KeyEvent p0); + public abstract boolean superDispatchKeyShortcutEvent(KeyEvent p0); + public abstract boolean superDispatchTouchEvent(MotionEvent p0); + public abstract boolean superDispatchTrackballEvent(MotionEvent p0); + public abstract int getNavigationBarColor(); + public abstract int getStatusBarColor(); + public abstract int getVolumeControlStream(); + public abstract void addContentView(View p0, ViewGroup.LayoutParams p1); + public abstract void closeAllPanels(); + public abstract void closePanel(int p0); + public abstract void invalidatePanelMenu(int p0); + public abstract void onConfigurationChanged(Configuration p0); + public abstract void openPanel(int p0, KeyEvent p1); + public abstract void restoreHierarchyState(Bundle p0); + public abstract void setBackgroundDrawable(Drawable p0); + public abstract void setChildDrawable(int p0, Drawable p1); + public abstract void setChildInt(int p0, int p1); + public abstract void setContentView(View p0); + public abstract void setContentView(View p0, ViewGroup.LayoutParams p1); + public abstract void setContentView(int p0); + public abstract void setDecorCaptionShade(int p0); + public abstract void setFeatureDrawable(int p0, Drawable p1); + public abstract void setFeatureDrawableAlpha(int p0, int p1); + public abstract void setFeatureDrawableResource(int p0, int p1); + public abstract void setFeatureDrawableUri(int p0, Uri p1); + public abstract void setFeatureInt(int p0, int p1); + public abstract void setNavigationBarColor(int p0); + public abstract void setResizingCaptionDrawable(Drawable p0); + public abstract void setStatusBarColor(int p0); + public abstract void setTitle(CharSequence p0); + public abstract void setTitleColor(int p0); + public abstract void setVolumeControlStream(int p0); + public abstract void takeInputQueue(InputQueue.Callback p0); + public abstract void takeKeyEvents(boolean p0); + public abstract void takeSurface(SurfaceHolder.Callback2 p0); + public abstract void togglePanel(int p0, KeyEvent p1); + public boolean getAllowEnterTransitionOverlap(){ return false; } + public boolean getAllowReturnTransitionOverlap(){ return false; } + public boolean getSharedElementsUseOverlay(){ return false; } + public boolean hasFeature(int p0){ return false; } + public boolean isNavigationBarContrastEnforced(){ return false; } + public boolean isStatusBarContrastEnforced(){ return false; } + public boolean isWideColorGamut(){ return false; } + public boolean requestFeature(int p0){ return false; } + public final T requireViewById(int p0){ return null; } + public final Context getContext(){ return null; } + public final TypedArray getWindowStyle(){ return null; } + public final Window getContainer(){ return null; } + public final Window.Callback getCallback(){ return null; } + public final WindowManager.LayoutParams getAttributes(){ return null; } + public final boolean hasChildren(){ return false; } + public final boolean isActive(){ return false; } + public final void addOnFrameMetricsAvailableListener(Window.OnFrameMetricsAvailableListener p0, Handler p1){} + public final void makeActive(){} + public final void removeOnFrameMetricsAvailableListener(Window.OnFrameMetricsAvailableListener p0){} + public final void setHideOverlayWindows(boolean p0){} + public final void setRestrictedCaptionAreaListener(Window.OnRestrictedCaptionAreaChangedListener p0){} + public int getColorMode(){ return 0; } + public int getNavigationBarDividerColor(){ return 0; } + public long getTransitionBackgroundFadeDuration(){ return 0; } + public static String NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME = null; + public static String STATUS_BAR_BACKGROUND_TRANSITION_NAME = null; + public static int DECOR_CAPTION_SHADE_AUTO = 0; + public static int DECOR_CAPTION_SHADE_DARK = 0; + public static int DECOR_CAPTION_SHADE_LIGHT = 0; + public static int FEATURE_ACTION_BAR = 0; + public static int FEATURE_ACTION_BAR_OVERLAY = 0; + public static int FEATURE_ACTION_MODE_OVERLAY = 0; + public static int FEATURE_ACTIVITY_TRANSITIONS = 0; + public static int FEATURE_CONTENT_TRANSITIONS = 0; + public static int FEATURE_CONTEXT_MENU = 0; + public static int FEATURE_CUSTOM_TITLE = 0; + public static int FEATURE_INDETERMINATE_PROGRESS = 0; + public static int FEATURE_LEFT_ICON = 0; + public static int FEATURE_NO_TITLE = 0; + public static int FEATURE_OPTIONS_PANEL = 0; + public static int FEATURE_PROGRESS = 0; + public static int FEATURE_RIGHT_ICON = 0; + public static int FEATURE_SWIPE_TO_DISMISS = 0; + public static int ID_ANDROID_CONTENT = 0; + public static int PROGRESS_END = 0; + public static int PROGRESS_INDETERMINATE_OFF = 0; + public static int PROGRESS_INDETERMINATE_ON = 0; + public static int PROGRESS_SECONDARY_END = 0; + public static int PROGRESS_SECONDARY_START = 0; + public static int PROGRESS_START = 0; + public static int PROGRESS_VISIBILITY_OFF = 0; + public static int PROGRESS_VISIBILITY_ON = 0; + public static int getDefaultFeatures(Context p0){ return 0; } + public void addFlags(int p0){} + public void clearFlags(int p0){} + public void injectInputEvent(InputEvent p0){} + public void registerScrollCaptureCallback(ScrollCaptureCallback p0){} + public void setAllowEnterTransitionOverlap(boolean p0){} + public void setAllowReturnTransitionOverlap(boolean p0){} + public void setAttributes(WindowManager.LayoutParams p0){} + public void setBackgroundBlurRadius(int p0){} + public void setBackgroundDrawableResource(int p0){} + public void setCallback(Window.Callback p0){} + public void setClipToOutline(boolean p0){} + public void setColorMode(int p0){} + public void setContainer(Window p0){} + public void setDecorFitsSystemWindows(boolean p0){} + public void setDimAmount(float p0){} + public void setElevation(float p0){} + public void setEnterTransition(Transition p0){} + public void setExitTransition(Transition p0){} + public void setFlags(int p0, int p1){} + public void setFormat(int p0){} + public void setGravity(int p0){} + public void setIcon(int p0){} + public void setLayout(int p0, int p1){} + public void setLocalFocus(boolean p0, boolean p1){} + public void setLogo(int p0){} + public void setMediaController(MediaController p0){} + public void setNavigationBarContrastEnforced(boolean p0){} + public void setNavigationBarDividerColor(int p0){} + public void setPreferMinimalPostProcessing(boolean p0){} + public void setReenterTransition(Transition p0){} + public void setReturnTransition(Transition p0){} + public void setSharedElementEnterTransition(Transition p0){} + public void setSharedElementExitTransition(Transition p0){} + public void setSharedElementReenterTransition(Transition p0){} + public void setSharedElementReturnTransition(Transition p0){} + public void setSharedElementsUseOverlay(boolean p0){} + public void setSoftInputMode(int p0){} + public void setStatusBarContrastEnforced(boolean p0){} + public void setSustainedPerformanceMode(boolean p0){} + public void setSystemGestureExclusionRects(List p0){} + public void setTransitionBackgroundFadeDuration(long p0){} + public void setTransitionManager(TransitionManager p0){} + public void setType(int p0){} + public void setUiOptions(int p0){} + public void setUiOptions(int p0, int p1){} + public void setWindowAnimations(int p0){} + public void setWindowManager(WindowManager p0, IBinder p1, String p2){} + public void setWindowManager(WindowManager p0, IBinder p1, String p2, boolean p3){} + public void unregisterScrollCaptureCallback(ScrollCaptureCallback p0){} + static public interface Callback + { + ActionMode onWindowStartingActionMode(ActionMode.Callback p0); + ActionMode onWindowStartingActionMode(ActionMode.Callback p0, int p1); + View onCreatePanelView(int p0); + boolean dispatchGenericMotionEvent(MotionEvent p0); + boolean dispatchKeyEvent(KeyEvent p0); + boolean dispatchKeyShortcutEvent(KeyEvent p0); + boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent p0); + boolean dispatchTouchEvent(MotionEvent p0); + boolean dispatchTrackballEvent(MotionEvent p0); + boolean onCreatePanelMenu(int p0, Menu p1); + boolean onMenuItemSelected(int p0, MenuItem p1); + boolean onMenuOpened(int p0, Menu p1); + boolean onPreparePanel(int p0, View p1, Menu p2); + boolean onSearchRequested(); + boolean onSearchRequested(SearchEvent p0); + default void onPointerCaptureChanged(boolean p0){} + default void onProvideKeyboardShortcuts(List p0, Menu p1, int p2){} + void onActionModeFinished(ActionMode p0); + void onActionModeStarted(ActionMode p0); + void onAttachedToWindow(); + void onContentChanged(); + void onDetachedFromWindow(); + void onPanelClosed(int p0, Menu p1); + void onWindowAttributesChanged(WindowManager.LayoutParams p0); + void onWindowFocusChanged(boolean p0); + } + static public interface OnFrameMetricsAvailableListener + { + void onFrameMetricsAvailable(Window p0, FrameMetrics p1, int p2); + } + static public interface OnRestrictedCaptionAreaChangedListener + { + void onRestrictedCaptionAreaChanged(Rect p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/WindowManager.java b/java/ql/test/stubs/google-android-9.0.0/android/view/WindowManager.java new file mode 100644 index 00000000000..ea0c98476ab --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/WindowManager.java @@ -0,0 +1,183 @@ +// Generated automatically from android.view.WindowManager for testing purposes + +package android.view; + +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.Display; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewManager; +import android.view.WindowMetrics; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +public interface WindowManager extends ViewManager +{ + Display getDefaultDisplay(); + default WindowMetrics getCurrentWindowMetrics(){ return null; } + default WindowMetrics getMaximumWindowMetrics(){ return null; } + default boolean isCrossWindowBlurEnabled(){ return false; } + default void addCrossWindowBlurEnabledListener(Consumer p0){} + default void addCrossWindowBlurEnabledListener(Executor p0, Consumer p1){} + default void removeCrossWindowBlurEnabledListener(Consumer p0){} + static public class LayoutParams extends ViewGroup.LayoutParams implements Parcelable + { + public IBinder token = null; + public LayoutParams(){} + public LayoutParams(Parcel p0){} + public LayoutParams(int p0){} + public LayoutParams(int p0, int p1){} + public LayoutParams(int p0, int p1, int p2){} + public LayoutParams(int p0, int p1, int p2, int p3, int p4){} + public LayoutParams(int p0, int p1, int p2, int p3, int p4, int p5, int p6){} + public String debug(String p0){ return null; } + public String packageName = null; + public String toString(){ return null; } + public boolean isFitInsetsIgnoringVisibility(){ return false; } + public boolean preferMinimalPostProcessing = false; + public final CharSequence getTitle(){ return null; } + public final int copyFrom(WindowManager.LayoutParams p0){ return 0; } + public final void setTitle(CharSequence p0){} + public float alpha = 0; + public float buttonBrightness = 0; + public float dimAmount = 0; + public float horizontalMargin = 0; + public float horizontalWeight = 0; + public float preferredRefreshRate = 0; + public float screenBrightness = 0; + public float verticalMargin = 0; + public float verticalWeight = 0; + public int describeContents(){ return 0; } + public int flags = 0; + public int format = 0; + public int getBlurBehindRadius(){ return 0; } + public int getColorMode(){ return 0; } + public int getFitInsetsSides(){ return 0; } + public int getFitInsetsTypes(){ return 0; } + public int gravity = 0; + public int layoutInDisplayCutoutMode = 0; + public int memoryType = 0; + public int preferredDisplayModeId = 0; + public int rotationAnimation = 0; + public int screenOrientation = 0; + public int softInputMode = 0; + public int systemUiVisibility = 0; + public int type = 0; + public int windowAnimations = 0; + public int x = 0; + public int y = 0; + public static Parcelable.Creator CREATOR = null; + public static boolean mayUseInputMethod(int p0){ return false; } + public static float BRIGHTNESS_OVERRIDE_FULL = 0; + public static float BRIGHTNESS_OVERRIDE_NONE = 0; + public static float BRIGHTNESS_OVERRIDE_OFF = 0; + public static int ALPHA_CHANGED = 0; + public static int ANIMATION_CHANGED = 0; + public static int DIM_AMOUNT_CHANGED = 0; + public static int FIRST_APPLICATION_WINDOW = 0; + public static int FIRST_SUB_WINDOW = 0; + public static int FIRST_SYSTEM_WINDOW = 0; + public static int FLAGS_CHANGED = 0; + public static int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0; + public static int FLAG_ALT_FOCUSABLE_IM = 0; + public static int FLAG_BLUR_BEHIND = 0; + public static int FLAG_DIM_BEHIND = 0; + public static int FLAG_DISMISS_KEYGUARD = 0; + public static int FLAG_DITHER = 0; + public static int FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS = 0; + public static int FLAG_FORCE_NOT_FULLSCREEN = 0; + public static int FLAG_FULLSCREEN = 0; + public static int FLAG_HARDWARE_ACCELERATED = 0; + public static int FLAG_IGNORE_CHEEK_PRESSES = 0; + public static int FLAG_KEEP_SCREEN_ON = 0; + public static int FLAG_LAYOUT_ATTACHED_IN_DECOR = 0; + public static int FLAG_LAYOUT_INSET_DECOR = 0; + public static int FLAG_LAYOUT_IN_OVERSCAN = 0; + public static int FLAG_LAYOUT_IN_SCREEN = 0; + public static int FLAG_LAYOUT_NO_LIMITS = 0; + public static int FLAG_LOCAL_FOCUS_MODE = 0; + public static int FLAG_NOT_FOCUSABLE = 0; + public static int FLAG_NOT_TOUCHABLE = 0; + public static int FLAG_NOT_TOUCH_MODAL = 0; + public static int FLAG_SCALED = 0; + public static int FLAG_SECURE = 0; + public static int FLAG_SHOW_WALLPAPER = 0; + public static int FLAG_SHOW_WHEN_LOCKED = 0; + public static int FLAG_SPLIT_TOUCH = 0; + public static int FLAG_TOUCHABLE_WHEN_WAKING = 0; + public static int FLAG_TRANSLUCENT_NAVIGATION = 0; + public static int FLAG_TRANSLUCENT_STATUS = 0; + public static int FLAG_TURN_SCREEN_ON = 0; + public static int FLAG_WATCH_OUTSIDE_TOUCH = 0; + public static int FORMAT_CHANGED = 0; + public static int LAST_APPLICATION_WINDOW = 0; + public static int LAST_SUB_WINDOW = 0; + public static int LAST_SYSTEM_WINDOW = 0; + public static int LAYOUT_CHANGED = 0; + public static int LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS = 0; + public static int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0; + public static int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 0; + public static int LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 0; + public static int MEMORY_TYPE_CHANGED = 0; + public static int MEMORY_TYPE_GPU = 0; + public static int MEMORY_TYPE_HARDWARE = 0; + public static int MEMORY_TYPE_NORMAL = 0; + public static int MEMORY_TYPE_PUSH_BUFFERS = 0; + public static int ROTATION_ANIMATION_CHANGED = 0; + public static int ROTATION_ANIMATION_CROSSFADE = 0; + public static int ROTATION_ANIMATION_JUMPCUT = 0; + public static int ROTATION_ANIMATION_ROTATE = 0; + public static int ROTATION_ANIMATION_SEAMLESS = 0; + public static int SCREEN_BRIGHTNESS_CHANGED = 0; + public static int SCREEN_ORIENTATION_CHANGED = 0; + public static int SOFT_INPUT_ADJUST_NOTHING = 0; + public static int SOFT_INPUT_ADJUST_PAN = 0; + public static int SOFT_INPUT_ADJUST_RESIZE = 0; + public static int SOFT_INPUT_ADJUST_UNSPECIFIED = 0; + public static int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0; + public static int SOFT_INPUT_MASK_ADJUST = 0; + public static int SOFT_INPUT_MASK_STATE = 0; + public static int SOFT_INPUT_MODE_CHANGED = 0; + public static int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 0; + public static int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 0; + public static int SOFT_INPUT_STATE_HIDDEN = 0; + public static int SOFT_INPUT_STATE_UNCHANGED = 0; + public static int SOFT_INPUT_STATE_UNSPECIFIED = 0; + public static int SOFT_INPUT_STATE_VISIBLE = 0; + public static int TITLE_CHANGED = 0; + public static int TYPE_ACCESSIBILITY_OVERLAY = 0; + public static int TYPE_APPLICATION = 0; + public static int TYPE_APPLICATION_ATTACHED_DIALOG = 0; + public static int TYPE_APPLICATION_MEDIA = 0; + public static int TYPE_APPLICATION_OVERLAY = 0; + public static int TYPE_APPLICATION_PANEL = 0; + public static int TYPE_APPLICATION_STARTING = 0; + public static int TYPE_APPLICATION_SUB_PANEL = 0; + public static int TYPE_BASE_APPLICATION = 0; + public static int TYPE_CHANGED = 0; + public static int TYPE_DRAWN_APPLICATION = 0; + public static int TYPE_INPUT_METHOD = 0; + public static int TYPE_INPUT_METHOD_DIALOG = 0; + public static int TYPE_KEYGUARD_DIALOG = 0; + public static int TYPE_PHONE = 0; + public static int TYPE_PRIORITY_PHONE = 0; + public static int TYPE_PRIVATE_PRESENTATION = 0; + public static int TYPE_SEARCH_BAR = 0; + public static int TYPE_STATUS_BAR = 0; + public static int TYPE_SYSTEM_ALERT = 0; + public static int TYPE_SYSTEM_DIALOG = 0; + public static int TYPE_SYSTEM_ERROR = 0; + public static int TYPE_SYSTEM_OVERLAY = 0; + public static int TYPE_TOAST = 0; + public static int TYPE_WALLPAPER = 0; + public void setBlurBehindRadius(int p0){} + public void setColorMode(int p0){} + public void setFitInsetsIgnoringVisibility(boolean p0){} + public void setFitInsetsSides(int p0){} + public void setFitInsetsTypes(int p0){} + public void writeToParcel(Parcel p0, int p1){} + } + void removeViewImmediate(View p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/WindowMetrics.java b/java/ql/test/stubs/google-android-9.0.0/android/view/WindowMetrics.java new file mode 100644 index 00000000000..9c9bc92058c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/WindowMetrics.java @@ -0,0 +1,14 @@ +// Generated automatically from android.view.WindowMetrics for testing purposes + +package android.view; + +import android.graphics.Rect; +import android.view.WindowInsets; + +public class WindowMetrics +{ + protected WindowMetrics() {} + public Rect getBounds(){ return null; } + public WindowInsets getWindowInsets(){ return null; } + public WindowMetrics(Rect p0, WindowInsets p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityEvent.java b/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityEvent.java index 1db7545960f..5e702246cb9 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityEvent.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityEvent.java @@ -28,6 +28,9 @@ public class AccessibilityEvent extends AccessibilityRecord implements Parcelabl public static Parcelable.Creator CREATOR = null; public static String eventTypeToString(int p0){ return null; } public static int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0; + public static int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0; + public static int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 0; + public static int CONTENT_CHANGE_TYPE_DRAG_STARTED = 0; public static int CONTENT_CHANGE_TYPE_PANE_APPEARED = 0; public static int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 0; public static int CONTENT_CHANGE_TYPE_PANE_TITLE = 0; diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityNodeInfo.java b/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityNodeInfo.java index 9724111622d..f781f0a6092 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityNodeInfo.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/accessibility/AccessibilityNodeInfo.java @@ -230,6 +230,9 @@ public class AccessibilityNodeInfo implements Parcelable public static AccessibilityNodeInfo.AccessibilityAction ACTION_COPY = null; public static AccessibilityNodeInfo.AccessibilityAction ACTION_CUT = null; public static AccessibilityNodeInfo.AccessibilityAction ACTION_DISMISS = null; + public static AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_CANCEL = null; + public static AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_DROP = null; + public static AccessibilityNodeInfo.AccessibilityAction ACTION_DRAG_START = null; public static AccessibilityNodeInfo.AccessibilityAction ACTION_EXPAND = null; public static AccessibilityNodeInfo.AccessibilityAction ACTION_FOCUS = null; public static AccessibilityNodeInfo.AccessibilityAction ACTION_HIDE_TOOLTIP = null; diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationAction.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationAction.java new file mode 100644 index 00000000000..fc35651b52d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationAction.java @@ -0,0 +1,31 @@ +// Generated automatically from android.view.textclassifier.ConversationAction for testing purposes + +package android.view.textclassifier; + +import android.app.RemoteAction; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class ConversationAction implements Parcelable +{ + protected ConversationAction() {} + public Bundle getExtras(){ return null; } + public CharSequence getTextReply(){ return null; } + public RemoteAction getAction(){ return null; } + public String getType(){ return null; } + public float getConfidenceScore(){ return 0; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static String TYPE_CALL_PHONE = null; + public static String TYPE_CREATE_REMINDER = null; + public static String TYPE_OPEN_URL = null; + public static String TYPE_SEND_EMAIL = null; + public static String TYPE_SEND_SMS = null; + public static String TYPE_SHARE_LOCATION = null; + public static String TYPE_TEXT_REPLY = null; + public static String TYPE_TRACK_FLIGHT = null; + public static String TYPE_VIEW_CALENDAR = null; + public static String TYPE_VIEW_MAP = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationActions.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationActions.java new file mode 100644 index 00000000000..ebe2eac2fd2 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/ConversationActions.java @@ -0,0 +1,51 @@ +// Generated automatically from android.view.textclassifier.ConversationActions for testing purposes + +package android.view.textclassifier; + +import android.app.Person; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.textclassifier.ConversationAction; +import android.view.textclassifier.TextClassifier; +import java.time.ZonedDateTime; +import java.util.List; + +public class ConversationActions implements Parcelable +{ + protected ConversationActions() {} + public ConversationActions(List p0, String p1){} + public List getConversationActions(){ return null; } + public String getId(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + static public class Message implements Parcelable + { + protected Message() {} + public Bundle getExtras(){ return null; } + public CharSequence getText(){ return null; } + public Person getAuthor(){ return null; } + public ZonedDateTime getReferenceTime(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static Person PERSON_USER_OTHERS = null; + public static Person PERSON_USER_SELF = null; + public void writeToParcel(Parcel p0, int p1){} + } + static public class Request implements Parcelable + { + protected Request() {} + public Bundle getExtras(){ return null; } + public List getConversation(){ return null; } + public List getHints(){ return null; } + public String getCallingPackageName(){ return null; } + public TextClassifier.EntityConfig getTypeConfig(){ return null; } + public int describeContents(){ return 0; } + public int getMaxSuggestions(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static String HINT_FOR_IN_APP = null; + public static String HINT_FOR_NOTIFICATION = null; + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/SelectionEvent.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/SelectionEvent.java new file mode 100644 index 00000000000..70d75c390b8 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/SelectionEvent.java @@ -0,0 +1,61 @@ +// Generated automatically from android.view.textclassifier.SelectionEvent for testing purposes + +package android.view.textclassifier; + +import android.os.Parcel; +import android.os.Parcelable; +import android.view.textclassifier.TextClassification; +import android.view.textclassifier.TextClassificationSessionId; +import android.view.textclassifier.TextSelection; + +public class SelectionEvent implements Parcelable +{ + public String getEntityType(){ return null; } + public String getPackageName(){ return null; } + public String getResultId(){ return null; } + public String getWidgetType(){ return null; } + public String getWidgetVersion(){ return null; } + public String toString(){ return null; } + public TextClassificationSessionId getSessionId(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getEnd(){ return 0; } + public int getEventIndex(){ return 0; } + public int getEventType(){ return 0; } + public int getInvocationMethod(){ return 0; } + public int getSmartEnd(){ return 0; } + public int getSmartStart(){ return 0; } + public int getStart(){ return 0; } + public int hashCode(){ return 0; } + public long getDurationSincePreviousEvent(){ return 0; } + public long getDurationSinceSessionStart(){ return 0; } + public long getEventTime(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static SelectionEvent createSelectionActionEvent(int p0, int p1, int p2){ return null; } + public static SelectionEvent createSelectionActionEvent(int p0, int p1, int p2, TextClassification p3){ return null; } + public static SelectionEvent createSelectionModifiedEvent(int p0, int p1){ return null; } + public static SelectionEvent createSelectionModifiedEvent(int p0, int p1, TextClassification p2){ return null; } + public static SelectionEvent createSelectionModifiedEvent(int p0, int p1, TextSelection p2){ return null; } + public static SelectionEvent createSelectionStartedEvent(int p0, int p1){ return null; } + public static boolean isTerminal(int p0){ return false; } + public static int ACTION_ABANDON = 0; + public static int ACTION_COPY = 0; + public static int ACTION_CUT = 0; + public static int ACTION_DRAG = 0; + public static int ACTION_OTHER = 0; + public static int ACTION_OVERTYPE = 0; + public static int ACTION_PASTE = 0; + public static int ACTION_RESET = 0; + public static int ACTION_SELECT_ALL = 0; + public static int ACTION_SHARE = 0; + public static int ACTION_SMART_SHARE = 0; + public static int EVENT_AUTO_SELECTION = 0; + public static int EVENT_SELECTION_MODIFIED = 0; + public static int EVENT_SELECTION_STARTED = 0; + public static int EVENT_SMART_SELECTION_MULTI = 0; + public static int EVENT_SMART_SELECTION_SINGLE = 0; + public static int INVOCATION_LINK = 0; + public static int INVOCATION_MANUAL = 0; + public static int INVOCATION_UNKNOWN = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassification.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassification.java new file mode 100644 index 00000000000..bd89b26c523 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassification.java @@ -0,0 +1,48 @@ +// Generated automatically from android.view.textclassifier.TextClassification for testing purposes + +package android.view.textclassifier; + +import android.app.RemoteAction; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.LocaleList; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.View; +import java.time.ZonedDateTime; +import java.util.List; + +public class TextClassification implements Parcelable +{ + protected TextClassification() {} + public Bundle getExtras(){ return null; } + public CharSequence getLabel(){ return null; } + public Drawable getIcon(){ return null; } + public Intent getIntent(){ return null; } + public List getActions(){ return null; } + public String getEntity(int p0){ return null; } + public String getId(){ return null; } + public String getText(){ return null; } + public String toString(){ return null; } + public View.OnClickListener getOnClickListener(){ return null; } + public float getConfidenceScore(String p0){ return 0; } + public int describeContents(){ return 0; } + public int getEntityCount(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + static public class Request implements Parcelable + { + protected Request() {} + public Bundle getExtras(){ return null; } + public CharSequence getText(){ return null; } + public LocaleList getDefaultLocales(){ return null; } + public String getCallingPackageName(){ return null; } + public ZonedDateTime getReferenceTime(){ return null; } + public int describeContents(){ return 0; } + public int getEndIndex(){ return 0; } + public int getStartIndex(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationContext.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationContext.java new file mode 100644 index 00000000000..a7ab6240ede --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationContext.java @@ -0,0 +1,18 @@ +// Generated automatically from android.view.textclassifier.TextClassificationContext for testing purposes + +package android.view.textclassifier; + +import android.os.Parcel; +import android.os.Parcelable; + +public class TextClassificationContext implements Parcelable +{ + protected TextClassificationContext() {} + public String getPackageName(){ return null; } + public String getWidgetType(){ return null; } + public String getWidgetVersion(){ return null; } + public String toString(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationSessionId.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationSessionId.java new file mode 100644 index 00000000000..fda8b2a0449 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassificationSessionId.java @@ -0,0 +1,17 @@ +// Generated automatically from android.view.textclassifier.TextClassificationSessionId for testing purposes + +package android.view.textclassifier; + +import android.os.Parcel; +import android.os.Parcelable; + +public class TextClassificationSessionId implements Parcelable +{ + public String getValue(){ return null; } + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifier.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifier.java new file mode 100644 index 00000000000..9f3daa67ea1 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifier.java @@ -0,0 +1,68 @@ +// Generated automatically from android.view.textclassifier.TextClassifier for testing purposes + +package android.view.textclassifier; + +import android.os.LocaleList; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.textclassifier.ConversationActions; +import android.view.textclassifier.SelectionEvent; +import android.view.textclassifier.TextClassification; +import android.view.textclassifier.TextClassifierEvent; +import android.view.textclassifier.TextLanguage; +import android.view.textclassifier.TextLinks; +import android.view.textclassifier.TextSelection; +import java.util.Collection; + +public interface TextClassifier +{ + default ConversationActions suggestConversationActions(ConversationActions.Request p0){ return null; } + default TextClassification classifyText(CharSequence p0, int p1, int p2, LocaleList p3){ return null; } + default TextClassification classifyText(TextClassification.Request p0){ return null; } + default TextLanguage detectLanguage(TextLanguage.Request p0){ return null; } + default TextLinks generateLinks(TextLinks.Request p0){ return null; } + default TextSelection suggestSelection(CharSequence p0, int p1, int p2, LocaleList p3){ return null; } + default TextSelection suggestSelection(TextSelection.Request p0){ return null; } + default boolean isDestroyed(){ return false; } + default int getMaxGenerateLinksTextLength(){ return 0; } + default void destroy(){} + default void onSelectionEvent(SelectionEvent p0){} + default void onTextClassifierEvent(TextClassifierEvent p0){} + static String EXTRA_FROM_TEXT_CLASSIFIER = null; + static String HINT_TEXT_IS_EDITABLE = null; + static String HINT_TEXT_IS_NOT_EDITABLE = null; + static String TYPE_ADDRESS = null; + static String TYPE_DATE = null; + static String TYPE_DATE_TIME = null; + static String TYPE_EMAIL = null; + static String TYPE_FLIGHT_NUMBER = null; + static String TYPE_OTHER = null; + static String TYPE_PHONE = null; + static String TYPE_UNKNOWN = null; + static String TYPE_URL = null; + static String WIDGET_TYPE_CLIPBOARD = null; + static String WIDGET_TYPE_CUSTOM_EDITTEXT = null; + static String WIDGET_TYPE_CUSTOM_TEXTVIEW = null; + static String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = null; + static String WIDGET_TYPE_EDITTEXT = null; + static String WIDGET_TYPE_EDIT_WEBVIEW = null; + static String WIDGET_TYPE_NOTIFICATION = null; + static String WIDGET_TYPE_TEXTVIEW = null; + static String WIDGET_TYPE_UNKNOWN = null; + static String WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = null; + static String WIDGET_TYPE_WEBVIEW = null; + static TextClassifier NO_OP = null; + static public class EntityConfig implements Parcelable + { + protected EntityConfig() {} + public Collection getHints(){ return null; } + public Collection resolveEntityListModifications(Collection p0){ return null; } + public boolean shouldIncludeTypesFromTextClassifier(){ return false; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static TextClassifier.EntityConfig create(Collection p0, Collection p1, Collection p2){ return null; } + public static TextClassifier.EntityConfig createWithExplicitEntityList(Collection p0){ return null; } + public static TextClassifier.EntityConfig createWithHints(Collection p0){ return null; } + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifierEvent.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifierEvent.java new file mode 100644 index 00000000000..ad18e8b78f5 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextClassifierEvent.java @@ -0,0 +1,54 @@ +// Generated automatically from android.view.textclassifier.TextClassifierEvent for testing purposes + +package android.view.textclassifier; + +import android.icu.util.ULocale; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.textclassifier.TextClassificationContext; + +abstract public class TextClassifierEvent implements Parcelable +{ + protected TextClassifierEvent() {} + public Bundle getExtras(){ return null; } + public String getModelName(){ return null; } + public String getResultId(){ return null; } + public String toString(){ return null; } + public String[] getEntityTypes(){ return null; } + public TextClassificationContext getEventContext(){ return null; } + public ULocale getLocale(){ return null; } + public float[] getScores(){ return null; } + public int describeContents(){ return 0; } + public int getEventCategory(){ return 0; } + public int getEventIndex(){ return 0; } + public int getEventType(){ return 0; } + public int[] getActionIndices(){ return null; } + public static Parcelable.Creator CREATOR = null; + public static int CATEGORY_CONVERSATION_ACTIONS = 0; + public static int CATEGORY_LANGUAGE_DETECTION = 0; + public static int CATEGORY_LINKIFY = 0; + public static int CATEGORY_SELECTION = 0; + public static int TYPE_ACTIONS_GENERATED = 0; + public static int TYPE_ACTIONS_SHOWN = 0; + public static int TYPE_AUTO_SELECTION = 0; + public static int TYPE_COPY_ACTION = 0; + public static int TYPE_CUT_ACTION = 0; + public static int TYPE_LINKS_GENERATED = 0; + public static int TYPE_LINK_CLICKED = 0; + public static int TYPE_MANUAL_REPLY = 0; + public static int TYPE_OTHER_ACTION = 0; + public static int TYPE_OVERTYPE = 0; + public static int TYPE_PASTE_ACTION = 0; + public static int TYPE_SELECTION_DESTROYED = 0; + public static int TYPE_SELECTION_DRAG = 0; + public static int TYPE_SELECTION_MODIFIED = 0; + public static int TYPE_SELECTION_RESET = 0; + public static int TYPE_SELECTION_STARTED = 0; + public static int TYPE_SELECT_ALL = 0; + public static int TYPE_SHARE_ACTION = 0; + public static int TYPE_SMART_ACTION = 0; + public static int TYPE_SMART_SELECTION_MULTI = 0; + public static int TYPE_SMART_SELECTION_SINGLE = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLanguage.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLanguage.java new file mode 100644 index 00000000000..8c04c6b43df --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLanguage.java @@ -0,0 +1,32 @@ +// Generated automatically from android.view.textclassifier.TextLanguage for testing purposes + +package android.view.textclassifier; + +import android.icu.util.ULocale; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; + +public class TextLanguage implements Parcelable +{ + protected TextLanguage() {} + public Bundle getExtras(){ return null; } + public String getId(){ return null; } + public String toString(){ return null; } + public ULocale getLocale(int p0){ return null; } + public float getConfidenceScore(ULocale p0){ return 0; } + public int describeContents(){ return 0; } + public int getLocaleHypothesisCount(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + static public class Request implements Parcelable + { + protected Request() {} + public Bundle getExtras(){ return null; } + public CharSequence getText(){ return null; } + public String getCallingPackageName(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLinks.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLinks.java index 597d751996e..1e36d672717 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLinks.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextLinks.java @@ -3,11 +3,14 @@ package android.view.textclassifier; import android.os.Bundle; +import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; import android.text.Spannable; import android.text.style.ClickableSpan; import android.view.View; +import android.view.textclassifier.TextClassifier; +import java.time.ZonedDateTime; import java.util.Collection; import java.util.function.Function; @@ -29,6 +32,19 @@ public class TextLinks implements Parcelable public static int STATUS_NO_LINKS_FOUND = 0; public static int STATUS_UNSUPPORTED_CHARACTER = 0; public void writeToParcel(Parcel p0, int p1){} + static public class Request implements Parcelable + { + protected Request() {} + public Bundle getExtras(){ return null; } + public CharSequence getText(){ return null; } + public LocaleList getDefaultLocales(){ return null; } + public String getCallingPackageName(){ return null; } + public TextClassifier.EntityConfig getEntityConfig(){ return null; } + public ZonedDateTime getReferenceTime(){ return null; } + public int describeContents(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + } static public class TextLink implements Parcelable { protected TextLink() {} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextSelection.java b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextSelection.java new file mode 100644 index 00000000000..015715305c4 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/view/textclassifier/TextSelection.java @@ -0,0 +1,40 @@ +// Generated automatically from android.view.textclassifier.TextSelection for testing purposes + +package android.view.textclassifier; + +import android.os.Bundle; +import android.os.LocaleList; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.textclassifier.TextClassification; + +public class TextSelection implements Parcelable +{ + protected TextSelection() {} + public Bundle getExtras(){ return null; } + public String getEntity(int p0){ return null; } + public String getId(){ return null; } + public String toString(){ return null; } + public TextClassification getTextClassification(){ return null; } + public float getConfidenceScore(String p0){ return 0; } + public int describeContents(){ return 0; } + public int getEntityCount(){ return 0; } + public int getSelectionEndIndex(){ return 0; } + public int getSelectionStartIndex(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + static public class Request implements Parcelable + { + protected Request() {} + public Bundle getExtras(){ return null; } + public CharSequence getText(){ return null; } + public LocaleList getDefaultLocales(){ return null; } + public String getCallingPackageName(){ return null; } + public boolean shouldIncludeTextClassification(){ return false; } + public int describeContents(){ return 0; } + public int getEndIndex(){ return 0; } + public int getStartIndex(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void writeToParcel(Parcel p0, int p1){} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/ClientCertRequest.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ClientCertRequest.java new file mode 100644 index 00000000000..0af0587e29d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ClientCertRequest.java @@ -0,0 +1,19 @@ +// Generated automatically from android.webkit.ClientCertRequest for testing purposes + +package android.webkit; + +import java.security.Principal; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +abstract public class ClientCertRequest +{ + public ClientCertRequest(){} + public abstract Principal[] getPrincipals(); + public abstract String getHost(); + public abstract String[] getKeyTypes(); + public abstract int getPort(); + public abstract void cancel(); + public abstract void ignore(); + public abstract void proceed(PrivateKey p0, X509Certificate[] p1); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/ConsoleMessage.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ConsoleMessage.java new file mode 100644 index 00000000000..9b50c6f7e6e --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ConsoleMessage.java @@ -0,0 +1,19 @@ +// Generated automatically from android.webkit.ConsoleMessage for testing purposes + +package android.webkit; + + +public class ConsoleMessage +{ + protected ConsoleMessage() {} + public ConsoleMessage(String p0, String p1, int p2, ConsoleMessage.MessageLevel p3){} + public ConsoleMessage.MessageLevel messageLevel(){ return null; } + public String message(){ return null; } + public String sourceId(){ return null; } + public int lineNumber(){ return 0; } + static public enum MessageLevel + { + DEBUG, ERROR, LOG, TIP, WARNING; + private MessageLevel() {} + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/DownloadListener.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/DownloadListener.java new file mode 100644 index 00000000000..2620faf369c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/DownloadListener.java @@ -0,0 +1,9 @@ +// Generated automatically from android.webkit.DownloadListener for testing purposes + +package android.webkit; + + +public interface DownloadListener +{ + void onDownloadStart(String p0, String p1, String p2, String p3, long p4); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/GeolocationPermissions.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/GeolocationPermissions.java new file mode 100644 index 00000000000..8edb6b2f3dd --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/GeolocationPermissions.java @@ -0,0 +1,20 @@ +// Generated automatically from android.webkit.GeolocationPermissions for testing purposes + +package android.webkit; + +import android.webkit.ValueCallback; +import java.util.Set; + +public class GeolocationPermissions +{ + public static GeolocationPermissions getInstance(){ return null; } + public void allow(String p0){} + public void clear(String p0){} + public void clearAll(){} + public void getAllowed(String p0, ValueCallback p1){} + public void getOrigins(ValueCallback> p0){} + static public interface Callback + { + void invoke(String p0, boolean p1, boolean p2); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/HttpAuthHandler.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/HttpAuthHandler.java new file mode 100644 index 00000000000..c2bde7e3ffc --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/HttpAuthHandler.java @@ -0,0 +1,12 @@ +// Generated automatically from android.webkit.HttpAuthHandler for testing purposes + +package android.webkit; + +import android.os.Handler; + +public class HttpAuthHandler extends Handler +{ + public boolean useHttpAuthUsernamePassword(){ return false; } + public void cancel(){} + public void proceed(String p0, String p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsPromptResult.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsPromptResult.java new file mode 100644 index 00000000000..835d76105a0 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsPromptResult.java @@ -0,0 +1,10 @@ +// Generated automatically from android.webkit.JsPromptResult for testing purposes + +package android.webkit; + +import android.webkit.JsResult; + +public class JsPromptResult extends JsResult +{ + public void confirm(String p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsResult.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsResult.java new file mode 100644 index 00000000000..c8168438130 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/JsResult.java @@ -0,0 +1,10 @@ +// Generated automatically from android.webkit.JsResult for testing purposes + +package android.webkit; + + +public class JsResult +{ + public final void cancel(){} + public final void confirm(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/PermissionRequest.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/PermissionRequest.java new file mode 100644 index 00000000000..da64b442084 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/PermissionRequest.java @@ -0,0 +1,18 @@ +// Generated automatically from android.webkit.PermissionRequest for testing purposes + +package android.webkit; + +import android.net.Uri; + +abstract public class PermissionRequest +{ + public PermissionRequest(){} + public abstract String[] getResources(); + public abstract Uri getOrigin(); + public abstract void deny(); + public abstract void grant(String[] p0); + public static String RESOURCE_AUDIO_CAPTURE = null; + public static String RESOURCE_MIDI_SYSEX = null; + public static String RESOURCE_PROTECTED_MEDIA_ID = null; + public static String RESOURCE_VIDEO_CAPTURE = null; +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/RenderProcessGoneDetail.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/RenderProcessGoneDetail.java new file mode 100644 index 00000000000..1e2086fdf3b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/RenderProcessGoneDetail.java @@ -0,0 +1,11 @@ +// Generated automatically from android.webkit.RenderProcessGoneDetail for testing purposes + +package android.webkit; + + +abstract public class RenderProcessGoneDetail +{ + public RenderProcessGoneDetail(){} + public abstract boolean didCrash(); + public abstract int rendererPriorityAtExit(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/SafeBrowsingResponse.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/SafeBrowsingResponse.java new file mode 100644 index 00000000000..751d7e76530 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/SafeBrowsingResponse.java @@ -0,0 +1,12 @@ +// Generated automatically from android.webkit.SafeBrowsingResponse for testing purposes + +package android.webkit; + + +abstract public class SafeBrowsingResponse +{ + public SafeBrowsingResponse(){} + public abstract void backToSafety(boolean p0); + public abstract void proceed(boolean p0); + public abstract void showInterstitial(boolean p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/SslErrorHandler.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/SslErrorHandler.java new file mode 100644 index 00000000000..ab56fed23fa --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/SslErrorHandler.java @@ -0,0 +1,11 @@ +// Generated automatically from android.webkit.SslErrorHandler for testing purposes + +package android.webkit; + +import android.os.Handler; + +public class SslErrorHandler extends Handler +{ + public void cancel(){} + public void proceed(){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/ValueCallback.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ValueCallback.java new file mode 100644 index 00000000000..0cdf8831825 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/ValueCallback.java @@ -0,0 +1,9 @@ +// Generated automatically from android.webkit.ValueCallback for testing purposes + +package android.webkit; + + +public interface ValueCallback +{ + void onReceiveValue(T p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebBackForwardList.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebBackForwardList.java new file mode 100644 index 00000000000..4fe7956b562 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebBackForwardList.java @@ -0,0 +1,16 @@ +// Generated automatically from android.webkit.WebBackForwardList for testing purposes + +package android.webkit; + +import android.webkit.WebHistoryItem; +import java.io.Serializable; + +abstract public class WebBackForwardList implements Cloneable, Serializable +{ + protected abstract WebBackForwardList clone(); + public WebBackForwardList(){} + public abstract WebHistoryItem getCurrentItem(); + public abstract WebHistoryItem getItemAtIndex(int p0); + public abstract int getCurrentIndex(); + public abstract int getSize(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebChromeClient.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebChromeClient.java new file mode 100644 index 00000000000..75b1f938d2d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebChromeClient.java @@ -0,0 +1,67 @@ +// Generated automatically from android.webkit.WebChromeClient for testing purposes + +package android.webkit; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Message; +import android.view.View; +import android.webkit.ConsoleMessage; +import android.webkit.GeolocationPermissions; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.PermissionRequest; +import android.webkit.ValueCallback; +import android.webkit.WebStorage; +import android.webkit.WebView; + +public class WebChromeClient +{ + abstract static public class FileChooserParams + { + public FileChooserParams(){} + public abstract CharSequence getTitle(); + public abstract Intent createIntent(); + public abstract String getFilenameHint(); + public abstract String[] getAcceptTypes(); + public abstract boolean isCaptureEnabled(); + public abstract int getMode(); + public static Uri[] parseResult(int p0, Intent p1){ return null; } + public static int MODE_OPEN = 0; + public static int MODE_OPEN_MULTIPLE = 0; + public static int MODE_SAVE = 0; + } + public Bitmap getDefaultVideoPoster(){ return null; } + public View getVideoLoadingProgressView(){ return null; } + public WebChromeClient(){} + public boolean onConsoleMessage(ConsoleMessage p0){ return false; } + public boolean onCreateWindow(WebView p0, boolean p1, boolean p2, Message p3){ return false; } + public boolean onJsAlert(WebView p0, String p1, String p2, JsResult p3){ return false; } + public boolean onJsBeforeUnload(WebView p0, String p1, String p2, JsResult p3){ return false; } + public boolean onJsConfirm(WebView p0, String p1, String p2, JsResult p3){ return false; } + public boolean onJsPrompt(WebView p0, String p1, String p2, String p3, JsPromptResult p4){ return false; } + public boolean onJsTimeout(){ return false; } + public boolean onShowFileChooser(WebView p0, ValueCallback p1, WebChromeClient.FileChooserParams p2){ return false; } + public void getVisitedHistory(ValueCallback p0){} + public void onCloseWindow(WebView p0){} + public void onConsoleMessage(String p0, int p1, String p2){} + public void onExceededDatabaseQuota(String p0, String p1, long p2, long p3, long p4, WebStorage.QuotaUpdater p5){} + public void onGeolocationPermissionsHidePrompt(){} + public void onGeolocationPermissionsShowPrompt(String p0, GeolocationPermissions.Callback p1){} + public void onHideCustomView(){} + public void onPermissionRequest(PermissionRequest p0){} + public void onPermissionRequestCanceled(PermissionRequest p0){} + public void onProgressChanged(WebView p0, int p1){} + public void onReachedMaxAppCacheSize(long p0, long p1, WebStorage.QuotaUpdater p2){} + public void onReceivedIcon(WebView p0, Bitmap p1){} + public void onReceivedTitle(WebView p0, String p1){} + public void onReceivedTouchIconUrl(WebView p0, String p1, boolean p2){} + public void onRequestFocus(WebView p0){} + public void onShowCustomView(View p0, WebChromeClient.CustomViewCallback p1){} + public void onShowCustomView(View p0, int p1, WebChromeClient.CustomViewCallback p2){} + static public interface CustomViewCallback + { + void onCustomViewHidden(); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebHistoryItem.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebHistoryItem.java new file mode 100644 index 00000000000..637467d888f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebHistoryItem.java @@ -0,0 +1,15 @@ +// Generated automatically from android.webkit.WebHistoryItem for testing purposes + +package android.webkit; + +import android.graphics.Bitmap; + +abstract public class WebHistoryItem implements Cloneable +{ + protected abstract WebHistoryItem clone(); + public WebHistoryItem(){} + public abstract Bitmap getFavicon(); + public abstract String getOriginalUrl(); + public abstract String getTitle(); + public abstract String getUrl(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessage.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessage.java new file mode 100644 index 00000000000..6e02214c1df --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessage.java @@ -0,0 +1,14 @@ +// Generated automatically from android.webkit.WebMessage for testing purposes + +package android.webkit; + +import android.webkit.WebMessagePort; + +public class WebMessage +{ + protected WebMessage() {} + public String getData(){ return null; } + public WebMessage(String p0){} + public WebMessage(String p0, WebMessagePort[] p1){} + public WebMessagePort[] getPorts(){ return null; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessagePort.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessagePort.java new file mode 100644 index 00000000000..05ece05e0a3 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebMessagePort.java @@ -0,0 +1,19 @@ +// Generated automatically from android.webkit.WebMessagePort for testing purposes + +package android.webkit; + +import android.os.Handler; +import android.webkit.WebMessage; + +abstract public class WebMessagePort +{ + abstract static public class WebMessageCallback + { + public WebMessageCallback(){} + public void onMessage(WebMessagePort p0, WebMessage p1){} + } + public abstract void close(); + public abstract void postMessage(WebMessage p0); + public abstract void setWebMessageCallback(WebMessagePort.WebMessageCallback p0); + public abstract void setWebMessageCallback(WebMessagePort.WebMessageCallback p0, Handler p1); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceError.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceError.java new file mode 100644 index 00000000000..115aaff3dec --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceError.java @@ -0,0 +1,10 @@ +// Generated automatically from android.webkit.WebResourceError for testing purposes + +package android.webkit; + + +abstract public class WebResourceError +{ + public abstract CharSequence getDescription(); + public abstract int getErrorCode(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java index 9732ca67ae0..7a195a1518a 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceRequest.java @@ -1,72 +1,16 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.webkit.WebResourceRequest for testing purposes + package android.webkit; import android.net.Uri; import java.util.Map; -/** - * Encompasses parameters to the {@link WebViewClient#shouldInterceptRequest} - * method. - */ -public interface WebResourceRequest { - /** - * Gets the URL for which the resource request was made. - * - * @return the URL for which the resource request was made. - */ - Uri getUrl(); - - /** - * Gets whether the request was made for the main frame. - * - * @return whether the request was made for the main frame. Will be - * {@code false} for iframes, for example. - */ - boolean isForMainFrame(); - - /** - * Gets whether the request was a result of a server-side redirect. - * - * @return whether the request was a result of a server-side redirect. - */ - boolean isRedirect(); - - /** - * Gets whether a gesture (such as a click) was associated with the request. For - * security reasons in certain situations this method may return {@code false} - * even though the sequence of events which caused the request to be created was - * initiated by a user gesture. - * - * @return whether a gesture was associated with the request. - */ - boolean hasGesture(); - - /** - * Gets the method associated with the request, for example "GET". - * - * @return the method associated with the request. - */ - String getMethod(); - - /** - * Gets the headers associated with the request. These are represented as a - * mapping of header name to header value. - * - * @return the headers associated with the request. - */ +public interface WebResourceRequest +{ Map getRequestHeaders(); -} \ No newline at end of file + String getMethod(); + Uri getUrl(); + boolean hasGesture(); + boolean isForMainFrame(); + boolean isRedirect(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java index 1a2ff3cc1da..9e0e0225644 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebResourceResponse.java @@ -1,173 +1,24 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.webkit.WebResourceResponse for testing purposes + package android.webkit; import java.io.InputStream; import java.util.Map; -/** - * Encapsulates a resource response. Applications can return an instance of this - * class from {@link WebViewClient#shouldInterceptRequest} to provide a custom - * response when the WebView requests a particular resource. - */ -public class WebResourceResponse { - /** - * Constructs a resource response with the given MIME type, encoding, and input - * stream. Callers must implement {@link InputStream#read(byte[]) - * InputStream.read(byte[])} for the input stream. - * - * @param mimeType the resource response's MIME type, for example text/html - * @param encoding the resource response's encoding - * @param data the input stream that provides the resource response's data. - * Must not be a StringBufferInputStream. - */ - public WebResourceResponse(String mimeType, String encoding, InputStream data) { - } - - /** - * Constructs a resource response with the given parameters. Callers must - * implement {@link InputStream#read(byte[]) InputStream.read(byte[])} for the - * input stream. - * - * @param mimeType the resource response's MIME type, for example - * text/html - * @param encoding the resource response's encoding - * @param statusCode the status code needs to be in the ranges [100, 299], - * [400, 599]. Causing a redirect by specifying a 3xx - * code is not supported. - * @param reasonPhrase the phrase describing the status code, for example - * "OK". Must be non-empty. - * @param responseHeaders the resource response's headers represented as a - * mapping of header name -> header value. - * @param data the input stream that provides the resource response's - * data. Must not be a StringBufferInputStream. - */ - public WebResourceResponse(String mimeType, String encoding, int statusCode, String reasonPhrase, - Map responseHeaders, InputStream data) { - } - - /** - * Sets the resource response's MIME type, for example "text/html". - * - * @param mimeType The resource response's MIME type - */ - public void setMimeType(String mimeType) { - } - - /** - * Gets the resource response's MIME type. - * - * @return The resource response's MIME type - */ - public String getMimeType() { - return null; - } - - /** - * Sets the resource response's encoding, for example "UTF-8". This is - * used to decode the data from the input stream. - * - * @param encoding The resource response's encoding - */ - public void setEncoding(String encoding) { - } - - /** - * Gets the resource response's encoding. - * - * @return The resource response's encoding - */ - public String getEncoding() { - return null; - } - - /** - * Sets the resource response's status code and reason phrase. - * - * @param statusCode the status code needs to be in the ranges [100, 299], - * [400, 599]. Causing a redirect by specifying a 3xx code - * is not supported. - * @param reasonPhrase the phrase describing the status code, for example "OK". - * Must be non-empty. - */ - public void setStatusCodeAndReasonPhrase(int statusCode, String reasonPhrase) { - } - - /** - * Gets the resource response's status code. - * - * @return The resource response's status code. - */ - public int getStatusCode() { - return -1; - } - - /** - * Gets the description of the resource response's status code. - * - * @return The description of the resource response's status code. - */ - public String getReasonPhrase() { - return null; - } - - /** - * Sets the headers for the resource response. - * - * @param headers Mapping of header name -> header value. - */ - public void setResponseHeaders(Map headers) { - } - - /** - * Gets the headers for the resource response. - * - * @return The headers for the resource response. - */ - public Map getResponseHeaders() { - return null; - } - - /** - * Sets the input stream that provides the resource response's data. Callers - * must implement {@link InputStream#read(byte[]) InputStream.read(byte[])}. - * - * @param data the input stream that provides the resource response's data. Must - * not be a StringBufferInputStream. - */ - public void setData(InputStream data) { - } - - /** - * Gets the input stream that provides the resource response's data. - * - * @return The input stream that provides the resource response's data - */ - public InputStream getData() { - return null; - } - - /** - * The internal version of the constructor that doesn't perform arguments - * checks. - * - * @hide - */ - public WebResourceResponse(boolean immutable, String mimeType, String encoding, int statusCode, String reasonPhrase, - Map responseHeaders, InputStream data) { - } - -} \ No newline at end of file +public class WebResourceResponse +{ + protected WebResourceResponse() {} + public InputStream getData(){ return null; } + public Map getResponseHeaders(){ return null; } + public String getEncoding(){ return null; } + public String getMimeType(){ return null; } + public String getReasonPhrase(){ return null; } + public WebResourceResponse(String p0, String p1, InputStream p2){} + public WebResourceResponse(String p0, String p1, int p2, String p3, Map p4, InputStream p5){} + public int getStatusCode(){ return 0; } + public void setData(InputStream p0){} + public void setEncoding(String p0){} + public void setMimeType(String p0){} + public void setResponseHeaders(Map p0){} + public void setStatusCodeAndReasonPhrase(int p0, String p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java index 33c9a1b8a57..60d01fe2b97 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebSettings.java @@ -1,1379 +1,150 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.webkit.WebSettings for testing purposes + package android.webkit; -import java.net.CookieManager; import android.content.Context; -/** - * Manages settings state for a WebView. When a WebView is first created, it - * obtains a set of default settings. These default settings will be returned - * from any getter call. A {@code WebSettings} object obtained from - * {@link WebView#getSettings()} is tied to the life of the WebView. If a - * WebView has been destroyed, any method call on {@code WebSettings} will throw - * an {@link IllegalStateException}. - */ -// This is an abstract base class: concrete WebViewProviders must -// create a class derived from this, and return an instance of it in the -// WebViewProvider.getWebSettingsProvider() method implementation. -public abstract class WebSettings { - /** - * Enum for controlling the layout of html. - *
      - *
    • {@code NORMAL} means no rendering changes. This is the recommended choice - * for maximum compatibility across different platforms and Android - * versions.
    • - *
    • {@code SINGLE_COLUMN} moves all content into one column that is the width - * of the view.
    • - *
    • {@code NARROW_COLUMNS} makes all columns no wider than the screen if - * possible. Only use this for API levels prior to - * {@link android.os.Build.VERSION_CODES#KITKAT}.
    • - *
    • {@code TEXT_AUTOSIZING} boosts font size of paragraphs based on - * heuristics to make the text readable when viewing a wide-viewport layout in - * the overview mode. It is recommended to enable zoom support - * {@link #setSupportZoom} when using this mode. Supported from API level - * {@link android.os.Build.VERSION_CODES#KITKAT}
    • - *
    - */ - // XXX: These must match LayoutAlgorithm in Settings.h in WebCore. - public enum LayoutAlgorithm { - NORMAL, - /** - * @deprecated This algorithm is now obsolete. - */ - @Deprecated - SINGLE_COLUMN, - /** - * @deprecated This algorithm is now obsolete. - */ - @Deprecated - NARROW_COLUMNS, TEXT_AUTOSIZING - } - - /** - * Enum for specifying the text size. - *
      - *
    • SMALLEST is 50%
    • - *
    • SMALLER is 75%
    • - *
    • NORMAL is 100%
    • - *
    • LARGER is 150%
    • - *
    • LARGEST is 200%
    • - *
    - * - * @deprecated Use {@link WebSettings#setTextZoom(int)} and - * {@link WebSettings#getTextZoom()} instead. - */ - @Deprecated - public enum TextSize { - SMALLEST(50), SMALLER(75), NORMAL(100), LARGER(150), LARGEST(200); - - TextSize(int size) { - value = size; - } - - int value; - } - - /** - * Enum for specifying the WebView's desired density. - *
      - *
    • {@code FAR} makes 100% looking like in 240dpi
    • - *
    • {@code MEDIUM} makes 100% looking like in 160dpi
    • - *
    • {@code CLOSE} makes 100% looking like in 120dpi
    • - *
    - */ - public enum ZoomDensity { - FAR(150), // 240dpi - MEDIUM(100), // 160dpi - CLOSE(75); // 120dpi - - ZoomDensity(int size) { - value = size; - } - - /** - * @hide Only for use by WebViewProvider implementations - */ - public int getValue() { - return value; - } - - int value; - } - - public @interface CacheMode { - } - - /** - * Default cache usage mode. If the navigation type doesn't impose any specific - * behavior, use cached resources when they are available and not expired, - * otherwise load resources from the network. Use with {@link #setCacheMode}. - */ - public static final int LOAD_DEFAULT = -1; - /** - * Normal cache usage mode. Use with {@link #setCacheMode}. - * - * @deprecated This value is obsolete, as from API level - * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and onwards it - * has the same effect as {@link #LOAD_DEFAULT}. - */ - @Deprecated - public static final int LOAD_NORMAL = 0; - /** - * Use cached resources when they are available, even if they have expired. - * Otherwise load resources from the network. Use with {@link #setCacheMode}. - */ - public static final int LOAD_CACHE_ELSE_NETWORK = 1; - /** - * Don't use the cache, load from the network. Use with {@link #setCacheMode}. - */ - public static final int LOAD_NO_CACHE = 2; - /** - * Don't use the network, load from the cache. Use with {@link #setCacheMode}. - */ - public static final int LOAD_CACHE_ONLY = 3; - - public enum RenderPriority { - NORMAL, HIGH, LOW - } - - /** - * The plugin state effects how plugins are treated on a page. ON means that any - * object will be loaded even if a plugin does not exist to handle the content. - * ON_DEMAND means that if there is a plugin installed that can handle the - * content, a placeholder is shown until the user clicks on the placeholder. - * Once clicked, the plugin will be enabled on the page. OFF means that all - * plugins will be turned off and any fallback content will be used. - */ - public enum PluginState { - ON, ON_DEMAND, OFF - } - - /** - * Used with {@link #setMixedContentMode} - * - * In this mode, the WebView will allow a secure origin to load content from any - * other origin, even if that origin is insecure. This is the least secure mode - * of operation for the WebView, and where possible apps should not set this - * mode. - */ - public static final int MIXED_CONTENT_ALWAYS_ALLOW = 0; - /** - * Used with {@link #setMixedContentMode} - * - * In this mode, the WebView will not allow a secure origin to load content from - * an insecure origin. This is the preferred and most secure mode of operation - * for the WebView and apps are strongly advised to use this mode. - */ - public static final int MIXED_CONTENT_NEVER_ALLOW = 1; - /** - * Used with {@link #setMixedContentMode} - * - * In this mode, the WebView will attempt to be compatible with the approach of - * a modern web browser with regard to mixed content. Some insecure content may - * be allowed to be loaded by a secure origin and other types of content will be - * blocked. The types of content are allowed or blocked may change release to - * release and are not explicitly defined. - * - * This mode is intended to be used by apps that are not in control of the - * content that they render but desire to operate in a reasonably secure - * environment. For highest security, apps are recommended to use - * {@link #MIXED_CONTENT_NEVER_ALLOW}. - */ - public static final int MIXED_CONTENT_COMPATIBILITY_MODE = 2; - - /** - * Enables dumping the pages navigation cache to a text file. The default is - * {@code false}. - * - * @deprecated This method is now obsolete. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract void setNavDump(boolean enabled); - - /** - * Gets whether dumping the navigation cache is enabled. - * - * @return whether dumping the navigation cache is enabled - * @see #setNavDump - * @deprecated This method is now obsolete. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract boolean getNavDump(); - - /** - * Sets whether the WebView should support zooming using its on-screen zoom - * controls and gestures. The particular zoom mechanisms that should be used can - * be set with {@link #setBuiltInZoomControls}. This setting does not affect - * zooming performed using the {@link WebView#zoomIn()} and - * {@link WebView#zoomOut()} methods. The default is {@code true}. - * - * @param support whether the WebView should support zoom - */ - public abstract void setSupportZoom(boolean support); - - /** - * Gets whether the WebView supports zoom. - * - * @return {@code true} if the WebView supports zoom - * @see #setSupportZoom - */ - public abstract boolean supportZoom(); - - /** - * Sets whether the WebView requires a user gesture to play media. The default - * is {@code true}. - * - * @param require whether the WebView requires a user gesture to play media - */ - public abstract void setMediaPlaybackRequiresUserGesture(boolean require); - - /** - * Gets whether the WebView requires a user gesture to play media. - * - * @return {@code true} if the WebView requires a user gesture to play media - * @see #setMediaPlaybackRequiresUserGesture - */ - public abstract boolean getMediaPlaybackRequiresUserGesture(); - - /** - * Sets whether the WebView should use its built-in zoom mechanisms. The - * built-in zoom mechanisms comprise on-screen zoom controls, which are - * displayed over the WebView's content, and the use of a pinch gesture to - * control zooming. Whether or not these on-screen controls are displayed can be - * set with {@link #setDisplayZoomControls}. The default is {@code false}. - *

    - * The built-in mechanisms are the only currently supported zoom mechanisms, so - * it is recommended that this setting is always enabled. - * - * @param enabled whether the WebView should use its built-in zoom mechanisms - */ - // This method was intended to select between the built-in zoom mechanisms - // and the separate zoom controls. The latter were obtained using - // {@link WebView#getZoomControls}, which is now hidden. - public abstract void setBuiltInZoomControls(boolean enabled); - - /** - * Gets whether the zoom mechanisms built into WebView are being used. - * - * @return {@code true} if the zoom mechanisms built into WebView are being used - * @see #setBuiltInZoomControls - */ - public abstract boolean getBuiltInZoomControls(); - - /** - * Sets whether the WebView should display on-screen zoom controls when using - * the built-in zoom mechanisms. See {@link #setBuiltInZoomControls}. The - * default is {@code true}. - * - * @param enabled whether the WebView should display on-screen zoom controls - */ - public abstract void setDisplayZoomControls(boolean enabled); - - /** - * Gets whether the WebView displays on-screen zoom controls when using the - * built-in zoom mechanisms. - * - * @return {@code true} if the WebView displays on-screen zoom controls when - * using the built-in zoom mechanisms - * @see #setDisplayZoomControls - */ - public abstract boolean getDisplayZoomControls(); - - /** - * Enables or disables file access within WebView. File access is enabled by - * default. Note that this enables or disables file system access only. Assets - * and resources are still accessible using file:///android_asset and - * file:///android_res. - */ - public abstract void setAllowFileAccess(boolean allow); - - /** - * Gets whether this WebView supports file access. - * - * @see #setAllowFileAccess - */ - public abstract boolean getAllowFileAccess(); - - /** - * Enables or disables content URL access within WebView. Content URL access - * allows WebView to load content from a content provider installed in the - * system. The default is enabled. - */ - public abstract void setAllowContentAccess(boolean allow); - - /** - * Gets whether this WebView supports content URL access. - * - * @see #setAllowContentAccess - */ - public abstract boolean getAllowContentAccess(); - - /** - * Sets whether the WebView loads pages in overview mode, that is, zooms out the - * content to fit on screen by width. This setting is taken into account when - * the content width is greater than the width of the WebView control, for - * example, when {@link #getUseWideViewPort} is enabled. The default is - * {@code false}. - */ - public abstract void setLoadWithOverviewMode(boolean overview); - - /** - * Gets whether this WebView loads pages in overview mode. - * - * @return whether this WebView loads pages in overview mode - * @see #setLoadWithOverviewMode - */ - public abstract boolean getLoadWithOverviewMode(); - - /** - * Sets whether the WebView will enable smooth transition while panning or - * zooming or while the window hosting the WebView does not have focus. If it is - * {@code true}, WebView will choose a solution to maximize the performance. - * e.g. the WebView's content may not be updated during the transition. If it is - * false, WebView will keep its fidelity. The default value is {@code false}. - * - * @deprecated This method is now obsolete, and will become a no-op in future. - */ - @Deprecated - public abstract void setEnableSmoothTransition(boolean enable); - - /** - * Gets whether the WebView enables smooth transition while panning or zooming. - * - * @see #setEnableSmoothTransition - * - * @deprecated This method is now obsolete, and will become a no-op in future. - */ - @Deprecated - public abstract boolean enableSmoothTransition(); - - /** - * Sets whether the WebView uses its background for over scroll background. If - * {@code true}, it will use the WebView's background. If {@code false}, it will - * use an internal pattern. Default is {@code true}. - * - * @deprecated This method is now obsolete. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract void setUseWebViewBackgroundForOverscrollBackground(boolean view); - - /** - * Gets whether this WebView uses WebView's background instead of internal - * pattern for over scroll background. - * - * @see #setUseWebViewBackgroundForOverscrollBackground - * @deprecated This method is now obsolete. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract boolean getUseWebViewBackgroundForOverscrollBackground(); - - /** - * Sets whether the WebView should save form data. In Android O, the platform - * has implemented a fully functional Autofill feature to store form data. - * Therefore, the Webview form data save feature is disabled. - * - * Note that the feature will continue to be supported on older versions of - * Android as before. - * - * This function does not have any effect. - */ - @Deprecated - public abstract void setSaveFormData(boolean save); - - /** - * Gets whether the WebView saves form data. - * - * @return whether the WebView saves form data - * @see #setSaveFormData - */ - @Deprecated - public abstract boolean getSaveFormData(); - - /** - * Sets whether the WebView should save passwords. The default is {@code true}. - * - * @deprecated Saving passwords in WebView will not be supported in future - * versions. - */ - @Deprecated - public abstract void setSavePassword(boolean save); - - /** - * Gets whether the WebView saves passwords. - * - * @return whether the WebView saves passwords - * @see #setSavePassword - * @deprecated Saving passwords in WebView will not be supported in future - * versions. - */ - @Deprecated - public abstract boolean getSavePassword(); - - /** - * Sets the text zoom of the page in percent. The default is 100. - * - * @param textZoom the text zoom in percent - */ - public abstract void setTextZoom(int textZoom); - - /** - * Gets the text zoom of the page in percent. - * - * @return the text zoom of the page in percent - * @see #setTextZoom - */ - public abstract int getTextZoom(); - - /** - * Sets policy for third party cookies. Developers should access this via - * {@link CookieManager#setShouldAcceptThirdPartyCookies}. - * - * @hide Internal API. - */ - public abstract void setAcceptThirdPartyCookies(boolean accept); - - /** - * Gets policy for third party cookies. Developers should access this via - * {@link CookieManager#getShouldAcceptThirdPartyCookies}. - * - * @hide Internal API - */ - public abstract boolean getAcceptThirdPartyCookies(); - - /** - * Sets the text size of the page. The default is {@link TextSize#NORMAL}. - * - * @param t the text size as a {@link TextSize} value - * @deprecated Use {@link #setTextZoom} instead. - */ - @Deprecated - public synchronized void setTextSize(TextSize t) { - setTextZoom(t.value); - } - - /** - * Gets the text size of the page. If the text size was previously specified in - * percent using {@link #setTextZoom}, this will return the closest matching - * {@link TextSize}. - * - * @return the text size as a {@link TextSize} value - * @see #setTextSize - * @deprecated Use {@link #getTextZoom} instead. - */ - @Deprecated - public synchronized TextSize getTextSize() { - return null; - } - - /** - * Sets the default zoom density of the page. This must be called from the UI - * thread. The default is {@link ZoomDensity#MEDIUM}. - * - * This setting is not recommended for use in new applications. If the WebView - * is utilized to display mobile-oriented pages, the desired effect can be - * achieved by adjusting 'width' and 'initial-scale' attributes of page's 'meta - * viewport' tag. For pages lacking the tag, - * {@link android.webkit.WebView#setInitialScale} and - * {@link #setUseWideViewPort} can be used. - * - * @param zoom the zoom density - * @deprecated This method is no longer supported, see the function - * documentation for recommended alternatives. - */ - @Deprecated - public abstract void setDefaultZoom(ZoomDensity zoom); - - /** - * Gets the default zoom density of the page. This should be called from the UI - * thread. - * - * This setting is not recommended for use in new applications. - * - * @return the zoom density - * @see #setDefaultZoom - * @deprecated Will only return the default value. - */ - @Deprecated - public abstract ZoomDensity getDefaultZoom(); - - /** - * Enables using light touches to make a selection and activate mouseovers. - * - * @deprecated From {@link android.os.Build.VERSION_CODES#JELLY_BEAN} this - * setting is obsolete and has no effect. - */ - @Deprecated - public abstract void setLightTouchEnabled(boolean enabled); - - /** - * Gets whether light touches are enabled. - * - * @see #setLightTouchEnabled - * @deprecated This setting is obsolete. - */ - @Deprecated - public abstract boolean getLightTouchEnabled(); - - /** - * Controlled a rendering optimization that is no longer present. Setting it now - * has no effect. - * - * @deprecated This setting now has no effect. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public void setUseDoubleTree(boolean use) { - // Specified to do nothing, so no need for derived classes to override. - } - - /** - * Controlled a rendering optimization that is no longer present. Setting it now - * has no effect. - * - * @deprecated This setting now has no effect. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public boolean getUseDoubleTree() { - // Returns false unconditionally, so no need for derived classes to override. - return false; - } - - /** - * Sets the user-agent string using an integer code. - *

      - *
    • 0 means the WebView should use an Android user-agent string
    • - *
    • 1 means the WebView should use a desktop user-agent string
    • - *
    - * Other values are ignored. The default is an Android user-agent string, i.e. - * code value 0. - * - * @param ua the integer code for the user-agent string - * @deprecated Please use {@link #setUserAgentString} instead. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract void setUserAgent(int ua); - - /** - * Gets the user-agent as an integer code. - *
      - *
    • -1 means the WebView is using a custom user-agent string set with - * {@link #setUserAgentString}
    • - *
    • 0 means the WebView should use an Android user-agent string
    • - *
    • 1 means the WebView should use a desktop user-agent string
    • - *
    - * - * @return the integer code for the user-agent string - * @see #setUserAgent - * @deprecated Please use {@link #getUserAgentString} instead. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} - */ - @Deprecated - public abstract int getUserAgent(); - - /** - * Sets whether the WebView should enable support for the "viewport" - * HTML meta tag or should use a wide viewport. When the value of the setting is - * {@code false}, the layout width is always set to the width of the WebView - * control in device-independent (CSS) pixels. When the value is {@code true} - * and the page contains the viewport meta tag, the value of the width specified - * in the tag is used. If the page does not contain the tag or does not provide - * a width, then a wide viewport will be used. - * - * @param use whether to enable support for the viewport meta tag - */ - public abstract void setUseWideViewPort(boolean use); - - /** - * Gets whether the WebView supports the "viewport" HTML meta tag or - * will use a wide viewport. - * - * @return {@code true} if the WebView supports the viewport meta tag - * @see #setUseWideViewPort - */ - public abstract boolean getUseWideViewPort(); - - /** - * Sets whether the WebView whether supports multiple windows. If set to true, - * {@link WebChromeClient#onCreateWindow} must be implemented by the host - * application. The default is {@code false}. - * - * @param support whether to support multiple windows - */ - public abstract void setSupportMultipleWindows(boolean support); - - /** - * Gets whether the WebView supports multiple windows. - * - * @return {@code true} if the WebView supports multiple windows - * @see #setSupportMultipleWindows - */ - public abstract boolean supportMultipleWindows(); - - /** - * Sets the underlying layout algorithm. This will cause a re-layout of the - * WebView. The default is {@link LayoutAlgorithm#NARROW_COLUMNS}. - * - * @param l the layout algorithm to use, as a {@link LayoutAlgorithm} value - */ - public abstract void setLayoutAlgorithm(LayoutAlgorithm l); - - /** - * Gets the current layout algorithm. - * - * @return the layout algorithm in use, as a {@link LayoutAlgorithm} value - * @see #setLayoutAlgorithm - */ - public abstract LayoutAlgorithm getLayoutAlgorithm(); - - /** - * Sets the standard font family name. The default is "sans-serif". - * - * @param font a font family name - */ - public abstract void setStandardFontFamily(String font); - - /** - * Gets the standard font family name. - * - * @return the standard font family name as a string - * @see #setStandardFontFamily - */ - public abstract String getStandardFontFamily(); - - /** - * Sets the fixed font family name. The default is "monospace". - * - * @param font a font family name - */ - public abstract void setFixedFontFamily(String font); - - /** - * Gets the fixed font family name. - * - * @return the fixed font family name as a string - * @see #setFixedFontFamily - */ - public abstract String getFixedFontFamily(); - - /** - * Sets the sans-serif font family name. The default is "sans-serif". - * - * @param font a font family name - */ - public abstract void setSansSerifFontFamily(String font); - - /** - * Gets the sans-serif font family name. - * - * @return the sans-serif font family name as a string - * @see #setSansSerifFontFamily - */ - public abstract String getSansSerifFontFamily(); - - /** - * Sets the serif font family name. The default is "sans-serif". - * - * @param font a font family name - */ - public abstract void setSerifFontFamily(String font); - - /** - * Gets the serif font family name. The default is "serif". - * - * @return the serif font family name as a string - * @see #setSerifFontFamily - */ - public abstract String getSerifFontFamily(); - - /** - * Sets the cursive font family name. The default is "cursive". - * - * @param font a font family name - */ - public abstract void setCursiveFontFamily(String font); - - /** - * Gets the cursive font family name. - * - * @return the cursive font family name as a string - * @see #setCursiveFontFamily - */ +abstract public class WebSettings +{ + public WebSettings(){} + public WebSettings.TextSize getTextSize(){ return null; } public abstract String getCursiveFontFamily(); - - /** - * Sets the fantasy font family name. The default is "fantasy". - * - * @param font a font family name - */ - public abstract void setFantasyFontFamily(String font); - - /** - * Gets the fantasy font family name. - * - * @return the fantasy font family name as a string - * @see #setFantasyFontFamily - */ - public abstract String getFantasyFontFamily(); - - /** - * Sets the minimum font size. The default is 8. - * - * @param size a non-negative integer between 1 and 72. Any number outside the - * specified range will be pinned. - */ - public abstract void setMinimumFontSize(int size); - - /** - * Gets the minimum font size. - * - * @return a non-negative integer between 1 and 72 - * @see #setMinimumFontSize - */ - public abstract int getMinimumFontSize(); - - /** - * Sets the minimum logical font size. The default is 8. - * - * @param size a non-negative integer between 1 and 72. Any number outside the - * specified range will be pinned. - */ - public abstract void setMinimumLogicalFontSize(int size); - - /** - * Gets the minimum logical font size. - * - * @return a non-negative integer between 1 and 72 - * @see #setMinimumLogicalFontSize - */ - public abstract int getMinimumLogicalFontSize(); - - /** - * Sets the default font size. The default is 16. - * - * @param size a non-negative integer between 1 and 72. Any number outside the - * specified range will be pinned. - */ - public abstract void setDefaultFontSize(int size); - - /** - * Gets the default font size. - * - * @return a non-negative integer between 1 and 72 - * @see #setDefaultFontSize - */ - public abstract int getDefaultFontSize(); - - /** - * Sets the default fixed font size. The default is 16. - * - * @param size a non-negative integer between 1 and 72. Any number outside the - * specified range will be pinned. - */ - public abstract void setDefaultFixedFontSize(int size); - - /** - * Gets the default fixed font size. - * - * @return a non-negative integer between 1 and 72 - * @see #setDefaultFixedFontSize - */ - public abstract int getDefaultFixedFontSize(); - - /** - * Sets whether the WebView should load image resources. Note that this method - * controls loading of all images, including those embedded using the data URI - * scheme. Use {@link #setBlockNetworkImage} to control loading only of images - * specified using network URI schemes. Note that if the value of this setting - * is changed from {@code false} to {@code true}, all images resources - * referenced by content currently displayed by the WebView are loaded - * automatically. The default is {@code true}. - * - * @param flag whether the WebView should load image resources - */ - public abstract void setLoadsImagesAutomatically(boolean flag); - - /** - * Gets whether the WebView loads image resources. This includes images embedded - * using the data URI scheme. - * - * @return {@code true} if the WebView loads image resources - * @see #setLoadsImagesAutomatically - */ - public abstract boolean getLoadsImagesAutomatically(); - - /** - * Sets whether the WebView should not load image resources from the network - * (resources accessed via http and https URI schemes). Note that this method - * has no effect unless {@link #getLoadsImagesAutomatically} returns - * {@code true}. Also note that disabling all network loads using - * {@link #setBlockNetworkLoads} will also prevent network images from loading, - * even if this flag is set to false. When the value of this setting is changed - * from {@code true} to {@code false}, network images resources referenced by - * content currently displayed by the WebView are fetched automatically. The - * default is {@code false}. - * - * @param flag whether the WebView should not load image resources from the - * network - * @see #setBlockNetworkLoads - */ - public abstract void setBlockNetworkImage(boolean flag); - - /** - * Gets whether the WebView does not load image resources from the network. - * - * @return {@code true} if the WebView does not load image resources from the - * network - * @see #setBlockNetworkImage - */ - public abstract boolean getBlockNetworkImage(); - - /** - * Sets whether the WebView should not load resources from the network. Use - * {@link #setBlockNetworkImage} to only avoid loading image resources. Note - * that if the value of this setting is changed from {@code true} to - * {@code false}, network resources referenced by content currently displayed by - * the WebView are not fetched until {@link android.webkit.WebView#reload} is - * called. If the application does not have the - * {@link android.Manifest.permission#INTERNET} permission, attempts to set a - * value of {@code false} will cause a {@link java.lang.SecurityException} to be - * thrown. The default value is {@code false} if the application has the - * {@link android.Manifest.permission#INTERNET} permission, otherwise it is - * {@code true}. - * - * @param flag {@code true} means block network loads by the WebView - * @see android.webkit.WebView#reload - */ - public abstract void setBlockNetworkLoads(boolean flag); - - /** - * Gets whether the WebView does not load any resources from the network. - * - * @return {@code true} if the WebView does not load any resources from the - * network - * @see #setBlockNetworkLoads - */ - public abstract boolean getBlockNetworkLoads(); - - /** - * Tells the WebView to enable JavaScript execution. The default is - * {@code false}. - * - * @param flag {@code true} if the WebView should execute JavaScript - */ - public abstract void setJavaScriptEnabled(boolean flag); - - /** - * Sets whether JavaScript running in the context of a file scheme URL should be - * allowed to access content from any origin. This includes access to content - * from other file scheme URLs. See {@link #setAllowFileAccessFromFileURLs}. To - * enable the most restrictive, and therefore secure policy, this setting should - * be disabled. Note that this setting affects only JavaScript access to file - * scheme resources. Other access to such resources, for example, from image - * HTML elements, is unaffected. To prevent possible violation of same domain - * policy when targeting - * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and earlier, - * you should explicitly set this value to {@code false}. - *

    - * The default value is {@code true} for apps targeting - * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, and - * {@code false} when targeting - * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} and above. - * - * @param flag whether JavaScript running in the context of a file scheme URL - * should be allowed to access content from any origin - */ - public abstract void setAllowUniversalAccessFromFileURLs(boolean flag); - - /** - * Sets whether JavaScript running in the context of a file scheme URL should be - * allowed to access content from other file scheme URLs. To enable the most - * restrictive, and therefore secure, policy this setting should be disabled. - * Note that the value of this setting is ignored if the value of - * {@link #getAllowUniversalAccessFromFileURLs} is {@code true}. Note too, that - * this setting affects only JavaScript access to file scheme resources. Other - * access to such resources, for example, from image HTML elements, is - * unaffected. To prevent possible violation of same domain policy when - * targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and - * earlier, you should explicitly set this value to {@code false}. - *

    - * The default value is {@code true} for apps targeting - * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below, and - * {@code false} when targeting - * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} and above. - * - * @param flag whether JavaScript running in the context of a file scheme URL - * should be allowed to access content from other file scheme URLs - */ - public abstract void setAllowFileAccessFromFileURLs(boolean flag); - - /** - * Sets whether the WebView should enable plugins. The default is {@code false}. - * - * @param flag {@code true} if plugins should be enabled - * @deprecated This method has been deprecated in favor of - * {@link #setPluginState} - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} - */ - @Deprecated - public abstract void setPluginsEnabled(boolean flag); - - /** - * Tells the WebView to enable, disable, or have plugins on demand. On demand - * mode means that if a plugin exists that can handle the embedded content, a - * placeholder icon will be shown instead of the plugin. When the placeholder is - * clicked, the plugin will be enabled. The default is {@link PluginState#OFF}. - * - * @param state a PluginState value - * @deprecated Plugins will not be supported in future, and should not be used. - */ - @Deprecated - public abstract void setPluginState(PluginState state); - - /** - * Sets a custom path to plugins used by the WebView. This method is obsolete - * since each plugin is now loaded from its own package. - * - * @param pluginsPath a String path to the directory containing plugins - * @deprecated This method is no longer used as plugins are loaded from their - * own APK via the system's package manager. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} - */ - @Deprecated - public void setPluginsPath(String pluginsPath) { - // Specified to do nothing, so no need for derived classes to override. - } - - /** - * Sets the path to where database storage API databases should be saved. In - * order for the database storage API to function correctly, this method must be - * called with a path to which the application can write. This method should - * only be called once: repeated calls are ignored. - * - * @param databasePath a path to the directory where databases should be saved. - * @deprecated Database paths are managed by the implementation and calling this - * method will have no effect. - */ - @Deprecated - public abstract void setDatabasePath(String databasePath); - - /** - * Sets the path where the Geolocation databases should be saved. In order for - * Geolocation permissions and cached positions to be persisted, this method - * must be called with a path to which the application can write. - * - * @param databasePath a path to the directory where databases should be saved. - * @deprecated Geolocation database are managed by the implementation and - * calling this method will have no effect. - */ - @Deprecated - public abstract void setGeolocationDatabasePath(String databasePath); - - /** - * Sets whether the Application Caches API should be enabled. The default is - * {@code false}. Note that in order for the Application Caches API to be - * enabled, a valid database path must also be supplied to - * {@link #setAppCachePath}. - * - * @param flag {@code true} if the WebView should enable Application Caches - */ - public abstract void setAppCacheEnabled(boolean flag); - - /** - * Sets the path to the Application Caches files. In order for the Application - * Caches API to be enabled, this method must be called with a path to which the - * application can write. This method should only be called once: repeated calls - * are ignored. - * - * @param appCachePath a String path to the directory containing Application - * Caches files. - * @see #setAppCacheEnabled - */ - public abstract void setAppCachePath(String appCachePath); - - /** - * Sets the maximum size for the Application Cache content. The passed size will - * be rounded to the nearest value that the database can support, so this should - * be viewed as a guide, not a hard limit. Setting the size to a value less than - * current database size does not cause the database to be trimmed. The default - * size is {@link Long#MAX_VALUE}. It is recommended to leave the maximum size - * set to the default value. - * - * @param appCacheMaxSize the maximum size in bytes - * @deprecated In future quota will be managed automatically. - */ - @Deprecated - public abstract void setAppCacheMaxSize(long appCacheMaxSize); - - /** - * Sets whether the database storage API is enabled. The default value is false. - * See also {@link #setDatabasePath} for how to correctly set up the database - * storage API. - * - * This setting is global in effect, across all WebView instances in a process. - * Note you should only modify this setting prior to making any WebView - * page load within a given process, as the WebView implementation may ignore - * changes to this setting after that point. - * - * @param flag {@code true} if the WebView should use the database storage API - */ - public abstract void setDatabaseEnabled(boolean flag); - - /** - * Sets whether the DOM storage API is enabled. The default value is - * {@code false}. - * - * @param flag {@code true} if the WebView should use the DOM storage API - */ - public abstract void setDomStorageEnabled(boolean flag); - - /** - * Gets whether the DOM Storage APIs are enabled. - * - * @return {@code true} if the DOM Storage APIs are enabled - * @see #setDomStorageEnabled - */ - public abstract boolean getDomStorageEnabled(); - - /** - * Gets the path to where database storage API databases are saved. - * - * @return the String path to the database storage API databases - * @see #setDatabasePath - * @deprecated Database paths are managed by the implementation this method is - * obsolete. - */ - @Deprecated public abstract String getDatabasePath(); - - /** - * Gets whether the database storage API is enabled. - * - * @return {@code true} if the database storage API is enabled - * @see #setDatabaseEnabled - */ - public abstract boolean getDatabaseEnabled(); - - /** - * Sets whether Geolocation is enabled. The default is {@code true}. - *

    - * Please note that in order for the Geolocation API to be usable by a page in - * the WebView, the following requirements must be met: - *

      - *
    • an application must have permission to access the device location, see - * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}, - * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}; - *
    • an application must provide an implementation of the - * {@link WebChromeClient#onGeolocationPermissionsShowPrompt} callback to - * receive notifications that a page is requesting access to location via the - * JavaScript Geolocation API. - *
    - *

    - * - * @param flag whether Geolocation should be enabled - */ - public abstract void setGeolocationEnabled(boolean flag); - - /** - * Gets whether JavaScript is enabled. - * - * @return {@code true} if JavaScript is enabled - * @see #setJavaScriptEnabled - */ - public abstract boolean getJavaScriptEnabled(); - - /** - * Gets whether JavaScript running in the context of a file scheme URL can - * access content from any origin. This includes access to content from other - * file scheme URLs. - * - * @return whether JavaScript running in the context of a file scheme URL can - * access content from any origin - * @see #setAllowUniversalAccessFromFileURLs - */ - public abstract boolean getAllowUniversalAccessFromFileURLs(); - - /** - * Gets whether JavaScript running in the context of a file scheme URL can - * access content from other file scheme URLs. - * - * @return whether JavaScript running in the context of a file scheme URL can - * access content from other file scheme URLs - * @see #setAllowFileAccessFromFileURLs - */ - public abstract boolean getAllowFileAccessFromFileURLs(); - - /** - * Gets whether plugins are enabled. - * - * @return {@code true} if plugins are enabled - * @see #setPluginsEnabled - * @deprecated This method has been replaced by {@link #getPluginState} - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} - */ - @Deprecated - public abstract boolean getPluginsEnabled(); - - /** - * Gets the current state regarding whether plugins are enabled. - * - * @return the plugin state as a {@link PluginState} value - * @see #setPluginState - * @deprecated Plugins will not be supported in future, and should not be used. - */ - @Deprecated - public abstract PluginState getPluginState(); - - /** - * Gets the directory that contains the plugin libraries. This method is - * obsolete since each plugin is now loaded from its own package. - * - * @return an empty string - * @deprecated This method is no longer used as plugins are loaded from their - * own APK via the system's package manager. - * @hide Since API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} - */ - @Deprecated - public String getPluginsPath() { - // Unconditionally returns empty string, so no need for derived classes to - // override. - return ""; - } - - /** - * Tells JavaScript to open windows automatically. This applies to the - * JavaScript function {@code window.open()}. The default is {@code false}. - * - * @param flag {@code true} if JavaScript can open windows automatically - */ - public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean flag); - - /** - * Gets whether JavaScript can open windows automatically. - * - * @return {@code true} if JavaScript can open windows automatically during - * {@code window.open()} - * @see #setJavaScriptCanOpenWindowsAutomatically - */ - public abstract boolean getJavaScriptCanOpenWindowsAutomatically(); - - /** - * Sets the default text encoding name to use when decoding html pages. The - * default is "UTF-8". - * - * @param encoding the text encoding name - */ - public abstract void setDefaultTextEncodingName(String encoding); - - /** - * Gets the default text encoding name. - * - * @return the default text encoding name as a string - * @see #setDefaultTextEncodingName - */ public abstract String getDefaultTextEncodingName(); - - /** - * Sets the WebView's user-agent string. If the string is {@code null} or empty, - * the system default value will be used. - * - * Note that starting from {@link android.os.Build.VERSION_CODES#KITKAT} Android - * version, changing the user-agent while loading a web page causes WebView to - * initiate loading once again. - * - * @param ua new user-agent string - */ - public abstract void setUserAgentString(String ua); - - /** - * Gets the WebView's user-agent string. - * - * @return the WebView's user-agent string - * @see #setUserAgentString - */ + public abstract String getFantasyFontFamily(); + public abstract String getFixedFontFamily(); + public abstract String getSansSerifFontFamily(); + public abstract String getSerifFontFamily(); + public abstract String getStandardFontFamily(); public abstract String getUserAgentString(); - - /** - * Returns the default User-Agent used by a WebView. An instance of WebView - * could use a different User-Agent if a call is made to - * {@link WebSettings#setUserAgentString(String)}. - * - * @param context a Context object used to access application assets - */ - public static String getDefaultUserAgent(Context context) { - return null; - } - - /** - * Tells the WebView whether it needs to set a node to have focus when - * {@link WebView#requestFocus(int, android.graphics.Rect)} is called. The - * default value is {@code true}. - * - * @param flag whether the WebView needs to set a node - */ - public abstract void setNeedInitialFocus(boolean flag); - - /** - * Sets the priority of the Render thread. Unlike the other settings, this one - * only needs to be called once per process. The default value is - * {@link RenderPriority#NORMAL}. - * - * @param priority the priority - * @deprecated It is not recommended to adjust thread priorities, and this will - * not be supported in future versions. - */ - @Deprecated - public abstract void setRenderPriority(RenderPriority priority); - - /** - * Overrides the way the cache is used. The way the cache is used is based on - * the navigation type. For a normal page load, the cache is checked and content - * is re-validated as needed. When navigating back, content is not revalidated, - * instead the content is just retrieved from the cache. This method allows the - * client to override this behavior by specifying one of {@link #LOAD_DEFAULT}, - * {@link #LOAD_CACHE_ELSE_NETWORK}, {@link #LOAD_NO_CACHE} or - * {@link #LOAD_CACHE_ONLY}. The default value is {@link #LOAD_DEFAULT}. - * - * @param mode the mode to use - */ - public abstract void setCacheMode(@CacheMode int mode); - - /** - * Gets the current setting for overriding the cache mode. - * - * @return the current setting for overriding the cache mode - * @see #setCacheMode - */ - public abstract int getCacheMode(); - - /** - * Configures the WebView's behavior when a secure origin attempts to load a - * resource from an insecure origin. - * - * By default, apps that target {@link android.os.Build.VERSION_CODES#KITKAT} or - * below default to {@link #MIXED_CONTENT_ALWAYS_ALLOW}. Apps targeting - * {@link android.os.Build.VERSION_CODES#LOLLIPOP} default to - * {@link #MIXED_CONTENT_NEVER_ALLOW}. - * - * The preferred and most secure mode of operation for the WebView is - * {@link #MIXED_CONTENT_NEVER_ALLOW} and use of - * {@link #MIXED_CONTENT_ALWAYS_ALLOW} is strongly discouraged. - * - * @param mode The mixed content mode to use. One of - * {@link #MIXED_CONTENT_NEVER_ALLOW}, - * {@link #MIXED_CONTENT_ALWAYS_ALLOW} or - * {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. - */ - public abstract void setMixedContentMode(int mode); - - /** - * Gets the current behavior of the WebView with regard to loading insecure - * content from a secure origin. - * - * @return The current setting, one of {@link #MIXED_CONTENT_NEVER_ALLOW}, - * {@link #MIXED_CONTENT_ALWAYS_ALLOW} or - * {@link #MIXED_CONTENT_COMPATIBILITY_MODE}. - */ - public abstract int getMixedContentMode(); - - /** - * Sets whether to use a video overlay for embedded encrypted video. In API - * levels prior to {@link android.os.Build.VERSION_CODES#LOLLIPOP}, encrypted - * video can only be rendered directly on a secure video surface, so it had been - * a hard problem to play encrypted video in HTML. When this flag is on, WebView - * can play encrypted video (MSE/EME) by using a video overlay (aka - * hole-punching) for videos embedded using HTML <video> tag.
    - * Caution: This setting is intended for use only in a narrow set of - * circumstances and apps should only enable it if they require playback of - * encrypted video content. It will impose the following limitations on the - * WebView: - *

      - *
    • Only one video overlay can be played at a time. - *
    • Changes made to position or dimensions of a video element may be - * propagated to the corresponding video overlay with a noticeable delay. - *
    • The video overlay is not visible to web APIs and as such may not interact - * with script or styling. For example, CSS styles applied to the <video> - * tag may be ignored. - *
    - * This is not an exhaustive set of constraints and it may vary with new - * versions of the WebView. - * - * @hide - */ - public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean flag); - - /** - * Gets whether a video overlay will be used for embedded encrypted video. - * - * @return {@code true} if WebView uses a video overlay for embedded encrypted - * video. - * @see #setVideoOverlayForEmbeddedEncryptedVideoEnabled - * @hide - */ - public abstract boolean getVideoOverlayForEmbeddedEncryptedVideoEnabled(); - - /** - * Sets whether this WebView should raster tiles when it is offscreen but - * attached to a window. Turning this on can avoid rendering artifacts when - * animating an offscreen WebView on-screen. Offscreen WebViews in this mode use - * more memory. The default value is false.
    - * Please follow these guidelines to limit memory usage: - *
      - *
    • WebView size should be not be larger than the device screen size. - *
    • Limit use of this mode to a small number of WebViews. Use it for visible - * WebViews and WebViews about to be animated to visible. - *
    - */ - public abstract void setOffscreenPreRaster(boolean enabled); - - /** - * Gets whether this WebView should raster tiles when it is offscreen but - * attached to a window. - * - * @return {@code true} if this WebView will raster tiles when it is offscreen - * but attached to a window. - */ + public abstract WebSettings.LayoutAlgorithm getLayoutAlgorithm(); + public abstract WebSettings.PluginState getPluginState(); + public abstract WebSettings.ZoomDensity getDefaultZoom(); + public abstract boolean enableSmoothTransition(); + public abstract boolean getAllowContentAccess(); + public abstract boolean getAllowFileAccess(); + public abstract boolean getAllowFileAccessFromFileURLs(); + public abstract boolean getAllowUniversalAccessFromFileURLs(); + public abstract boolean getBlockNetworkImage(); + public abstract boolean getBlockNetworkLoads(); + public abstract boolean getBuiltInZoomControls(); + public abstract boolean getDatabaseEnabled(); + public abstract boolean getDisplayZoomControls(); + public abstract boolean getDomStorageEnabled(); + public abstract boolean getJavaScriptCanOpenWindowsAutomatically(); + public abstract boolean getJavaScriptEnabled(); + public abstract boolean getLightTouchEnabled(); + public abstract boolean getLoadWithOverviewMode(); + public abstract boolean getLoadsImagesAutomatically(); + public abstract boolean getMediaPlaybackRequiresUserGesture(); public abstract boolean getOffscreenPreRaster(); - - /** - * Sets whether Safe Browsing is enabled. Safe Browsing allows WebView to - * protect against malware and phishing attacks by verifying the links. - * - *

    - * Safe Browsing can be disabled for all WebViews using a manifest tag (read - * general Safe - * Browsing info). The manifest tag has a lower precedence than this API. - * - *

    - * Safe Browsing is enabled by default for devices which support it. - * - * @param enabled Whether Safe Browsing is enabled. - */ - public abstract void setSafeBrowsingEnabled(boolean enabled); - - /** - * Gets whether Safe Browsing is enabled. See {@link #setSafeBrowsingEnabled}. - * - * @return {@code true} if Safe Browsing is enabled and {@code false} otherwise. - */ public abstract boolean getSafeBrowsingEnabled(); + public abstract boolean getSaveFormData(); + public abstract boolean getSavePassword(); + public abstract boolean getUseWideViewPort(); + public abstract boolean supportMultipleWindows(); + public abstract boolean supportZoom(); + public abstract int getCacheMode(); + public abstract int getDefaultFixedFontSize(); + public abstract int getDefaultFontSize(); + public abstract int getDisabledActionModeMenuItems(); + public abstract int getMinimumFontSize(); + public abstract int getMinimumLogicalFontSize(); + public abstract int getMixedContentMode(); + public abstract int getTextZoom(); + public abstract void setAllowContentAccess(boolean p0); + public abstract void setAllowFileAccess(boolean p0); + public abstract void setAllowFileAccessFromFileURLs(boolean p0); + public abstract void setAllowUniversalAccessFromFileURLs(boolean p0); + public abstract void setAppCacheEnabled(boolean p0); + public abstract void setAppCacheMaxSize(long p0); + public abstract void setAppCachePath(String p0); + public abstract void setBlockNetworkImage(boolean p0); + public abstract void setBlockNetworkLoads(boolean p0); + public abstract void setBuiltInZoomControls(boolean p0); + public abstract void setCacheMode(int p0); + public abstract void setCursiveFontFamily(String p0); + public abstract void setDatabaseEnabled(boolean p0); + public abstract void setDatabasePath(String p0); + public abstract void setDefaultFixedFontSize(int p0); + public abstract void setDefaultFontSize(int p0); + public abstract void setDefaultTextEncodingName(String p0); + public abstract void setDefaultZoom(WebSettings.ZoomDensity p0); + public abstract void setDisabledActionModeMenuItems(int p0); + public abstract void setDisplayZoomControls(boolean p0); + public abstract void setDomStorageEnabled(boolean p0); + public abstract void setEnableSmoothTransition(boolean p0); + public abstract void setFantasyFontFamily(String p0); + public abstract void setFixedFontFamily(String p0); + public abstract void setGeolocationDatabasePath(String p0); + public abstract void setGeolocationEnabled(boolean p0); + public abstract void setJavaScriptCanOpenWindowsAutomatically(boolean p0); + public abstract void setJavaScriptEnabled(boolean p0); + public abstract void setLayoutAlgorithm(WebSettings.LayoutAlgorithm p0); + public abstract void setLightTouchEnabled(boolean p0); + public abstract void setLoadWithOverviewMode(boolean p0); + public abstract void setLoadsImagesAutomatically(boolean p0); + public abstract void setMediaPlaybackRequiresUserGesture(boolean p0); + public abstract void setMinimumFontSize(int p0); + public abstract void setMinimumLogicalFontSize(int p0); + public abstract void setMixedContentMode(int p0); + public abstract void setNeedInitialFocus(boolean p0); + public abstract void setOffscreenPreRaster(boolean p0); + public abstract void setPluginState(WebSettings.PluginState p0); + public abstract void setRenderPriority(WebSettings.RenderPriority p0); + public abstract void setSafeBrowsingEnabled(boolean p0); + public abstract void setSansSerifFontFamily(String p0); + public abstract void setSaveFormData(boolean p0); + public abstract void setSavePassword(boolean p0); + public abstract void setSerifFontFamily(String p0); + public abstract void setStandardFontFamily(String p0); + public abstract void setSupportMultipleWindows(boolean p0); + public abstract void setSupportZoom(boolean p0); + public abstract void setTextZoom(int p0); + public abstract void setUseWideViewPort(boolean p0); + public abstract void setUserAgentString(String p0); + public int getForceDark(){ return 0; } + public static String getDefaultUserAgent(Context p0){ return null; } + public static int FORCE_DARK_AUTO = 0; + public static int FORCE_DARK_OFF = 0; + public static int FORCE_DARK_ON = 0; + public static int LOAD_CACHE_ELSE_NETWORK = 0; + public static int LOAD_CACHE_ONLY = 0; + public static int LOAD_DEFAULT = 0; + public static int LOAD_NORMAL = 0; + public static int LOAD_NO_CACHE = 0; + public static int MENU_ITEM_NONE = 0; + public static int MENU_ITEM_PROCESS_TEXT = 0; + public static int MENU_ITEM_SHARE = 0; + public static int MENU_ITEM_WEB_SEARCH = 0; + public static int MIXED_CONTENT_ALWAYS_ALLOW = 0; + public static int MIXED_CONTENT_COMPATIBILITY_MODE = 0; + public static int MIXED_CONTENT_NEVER_ALLOW = 0; + public void setForceDark(int p0){} + public void setTextSize(WebSettings.TextSize p0){} + static public enum LayoutAlgorithm + { + NARROW_COLUMNS, NORMAL, SINGLE_COLUMN, TEXT_AUTOSIZING; + private LayoutAlgorithm() {} + } + static public enum PluginState + { + OFF, ON, ON_DEMAND; + private PluginState() {} + } + static public enum RenderPriority + { + HIGH, LOW, NORMAL; + private RenderPriority() {} + } + static public enum TextSize + { + LARGER, LARGEST, NORMAL, SMALLER, SMALLEST; + private TextSize() {} + } + static public enum ZoomDensity + { + CLOSE, FAR, MEDIUM; + private ZoomDensity() {} + } } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebStorage.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebStorage.java new file mode 100644 index 00000000000..c63a282b454 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebStorage.java @@ -0,0 +1,21 @@ +// Generated automatically from android.webkit.WebStorage for testing purposes + +package android.webkit; + +import android.webkit.ValueCallback; +import java.util.Map; + +public class WebStorage +{ + public static WebStorage getInstance(){ return null; } + public void deleteAllData(){} + public void deleteOrigin(String p0){} + public void getOrigins(ValueCallback p0){} + public void getQuotaForOrigin(String p0, ValueCallback p1){} + public void getUsageForOrigin(String p0, ValueCallback p1){} + public void setQuotaForOrigin(String p0, long p1){} + static public interface QuotaUpdater + { + void updateQuota(long p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java index 3065a9df966..b654cc05f61 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebView.java @@ -1,151 +1,259 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ +// Generated automatically from android.webkit.WebView for testing purposes package android.webkit; import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.res.Configuration; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Picture; +import android.graphics.Rect; +import android.net.Uri; +import android.net.http.SslCertificate; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.print.PrintDocumentAdapter; +import android.util.AttributeSet; +import android.util.LongSparseArray; +import android.util.SparseArray; +import android.view.DragEvent; +import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; +import android.view.ViewStructure; +import android.view.ViewTreeObserver; +import android.view.WindowInsets; +import android.view.accessibility.AccessibilityNodeProvider; +import android.view.autofill.AutofillId; +import android.view.autofill.AutofillValue; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.textclassifier.TextClassifier; +import android.view.translation.TranslationCapability; +import android.view.translation.ViewTranslationRequest; +import android.view.translation.ViewTranslationResponse; +import android.webkit.DownloadListener; +import android.webkit.ValueCallback; +import android.webkit.WebBackForwardList; +import android.webkit.WebChromeClient; +import android.webkit.WebMessage; +import android.webkit.WebMessagePort; +import android.webkit.WebSettings; +import android.webkit.WebViewClient; +import android.webkit.WebViewRenderProcess; +import android.webkit.WebViewRenderProcessClient; +import android.widget.AbsoluteLayout; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.function.Consumer; -public class WebView extends View { - - public WebView(Context context) { - super(context); +public class WebView extends AbsoluteLayout implements ViewGroup.OnHierarchyChangeListener, ViewTreeObserver.OnGlobalFocusChangeListener +{ + protected WebView() {} + abstract static public class VisualStateCallback + { + public VisualStateCallback(){} + public abstract void onComplete(long p0); } - - public void setHorizontalScrollbarOverlay(boolean overlay) {} - - public void setVerticalScrollbarOverlay(boolean overlay) {} - - public boolean overlayHorizontalScrollbar() { - return false; + protected int computeHorizontalScrollOffset(){ return 0; } + protected int computeHorizontalScrollRange(){ return 0; } + protected int computeVerticalScrollExtent(){ return 0; } + protected int computeVerticalScrollOffset(){ return 0; } + protected int computeVerticalScrollRange(){ return 0; } + protected void dispatchDraw(Canvas p0){} + protected void onAttachedToWindow(){} + protected void onConfigurationChanged(Configuration p0){} + protected void onDraw(Canvas p0){} + protected void onFocusChanged(boolean p0, int p1, Rect p2){} + protected void onMeasure(int p0, int p1){} + protected void onOverScrolled(int p0, int p1, boolean p2, boolean p3){} + protected void onScrollChanged(int p0, int p1, int p2, int p3){} + protected void onSizeChanged(int p0, int p1, int p2, int p3){} + protected void onVisibilityChanged(View p0, int p1){} + protected void onWindowVisibilityChanged(int p0){} + public AccessibilityNodeProvider getAccessibilityNodeProvider(){ return null; } + public Bitmap getFavicon(){ return null; } + public CharSequence getAccessibilityClassName(){ return null; } + public Handler getHandler(){ return null; } + public InputConnection onCreateInputConnection(EditorInfo p0){ return null; } + public Looper getWebViewLooper(){ return null; } + public Picture capturePicture(){ return null; } + public PrintDocumentAdapter createPrintDocumentAdapter(){ return null; } + public PrintDocumentAdapter createPrintDocumentAdapter(String p0){ return null; } + public SslCertificate getCertificate(){ return null; } + public String getOriginalUrl(){ return null; } + public String getTitle(){ return null; } + public String getUrl(){ return null; } + public String[] getHttpAuthUsernamePassword(String p0, String p1){ return null; } + public TextClassifier getTextClassifier(){ return null; } + public View findFocus(){ return null; } + public WebBackForwardList copyBackForwardList(){ return null; } + public WebBackForwardList restoreState(Bundle p0){ return null; } + public WebBackForwardList saveState(Bundle p0){ return null; } + public WebChromeClient getWebChromeClient(){ return null; } + public WebMessagePort[] createWebMessageChannel(){ return null; } + public WebSettings getSettings(){ return null; } + public WebView(Context p0){} + public WebView(Context p0, AttributeSet p1){} + public WebView(Context p0, AttributeSet p1, int p2){} + public WebView(Context p0, AttributeSet p1, int p2, boolean p3){} + public WebView(Context p0, AttributeSet p1, int p2, int p3){} + public WebView.HitTestResult getHitTestResult(){ return null; } + public WebViewClient getWebViewClient(){ return null; } + public WebViewRenderProcess getWebViewRenderProcess(){ return null; } + public WebViewRenderProcessClient getWebViewRenderProcessClient(){ return null; } + public WindowInsets onApplyWindowInsets(WindowInsets p0){ return null; } + public boolean canGoBack(){ return false; } + public boolean canGoBackOrForward(int p0){ return false; } + public boolean canGoForward(){ return false; } + public boolean canZoomIn(){ return false; } + public boolean canZoomOut(){ return false; } + public boolean dispatchKeyEvent(KeyEvent p0){ return false; } + public boolean getRendererPriorityWaivedWhenNotVisible(){ return false; } + public boolean isPrivateBrowsingEnabled(){ return false; } + public boolean isVisibleToUserForAutofill(int p0){ return false; } + public boolean onCheckIsTextEditor(){ return false; } + public boolean onDragEvent(DragEvent p0){ return false; } + public boolean onGenericMotionEvent(MotionEvent p0){ return false; } + public boolean onHoverEvent(MotionEvent p0){ return false; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyMultiple(int p0, int p1, KeyEvent p2){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public boolean onTouchEvent(MotionEvent p0){ return false; } + public boolean onTrackballEvent(MotionEvent p0){ return false; } + public boolean overlayHorizontalScrollbar(){ return false; } + public boolean overlayVerticalScrollbar(){ return false; } + public boolean pageDown(boolean p0){ return false; } + public boolean pageUp(boolean p0){ return false; } + public boolean performLongClick(){ return false; } + public boolean requestChildRectangleOnScreen(View p0, Rect p1, boolean p2){ return false; } + public boolean requestFocus(int p0, Rect p1){ return false; } + public boolean shouldDelayChildPressedState(){ return false; } + public boolean showFindDialog(String p0, boolean p1){ return false; } + public boolean zoomIn(){ return false; } + public boolean zoomOut(){ return false; } + public float getScale(){ return 0; } + public int findAll(String p0){ return 0; } + public int getContentHeight(){ return 0; } + public int getProgress(){ return 0; } + public int getRendererRequestedPriority(){ return 0; } + public static ClassLoader getWebViewClassLoader(){ return null; } + public static PackageInfo getCurrentWebViewPackage(){ return null; } + public static String SCHEME_GEO = null; + public static String SCHEME_MAILTO = null; + public static String SCHEME_TEL = null; + public static String findAddress(String p0){ return null; } + public static Uri getSafeBrowsingPrivacyPolicyUrl(){ return null; } + public static int RENDERER_PRIORITY_BOUND = 0; + public static int RENDERER_PRIORITY_IMPORTANT = 0; + public static int RENDERER_PRIORITY_WAIVED = 0; + public static void clearClientCertPreferences(Runnable p0){} + public static void disableWebView(){} + public static void enableSlowWholeDocumentDraw(){} + public static void setDataDirectorySuffix(String p0){} + public static void setSafeBrowsingWhitelist(List p0, ValueCallback p1){} + public static void setWebContentsDebuggingEnabled(boolean p0){} + public static void startSafeBrowsing(Context p0, ValueCallback p1){} + public void addJavascriptInterface(Object p0, String p1){} + public void autofill(SparseArray p0){} + public void clearCache(boolean p0){} + public void clearFormData(){} + public void clearHistory(){} + public void clearMatches(){} + public void clearSslPreferences(){} + public void clearView(){} + public void computeScroll(){} + public void destroy(){} + public void dispatchCreateViewTranslationRequest(Map p0, int[] p1, TranslationCapability p2, List p3){} + public void documentHasImages(Message p0){} + public void evaluateJavascript(String p0, ValueCallback p1){} + public void findAllAsync(String p0){} + public void findNext(boolean p0){} + public void flingScroll(int p0, int p1){} + public void freeMemory(){} + public void goBack(){} + public void goBackOrForward(int p0){} + public void goForward(){} + public void invokeZoomPicker(){} + public void loadData(String p0, String p1, String p2){} + public void loadDataWithBaseURL(String p0, String p1, String p2, String p3, String p4){} + public void loadUrl(String p0){} + public void loadUrl(String p0, Map p1){} + public void onChildViewAdded(View p0, View p1){} + public void onChildViewRemoved(View p0, View p1){} + public void onCreateVirtualViewTranslationRequests(long[] p0, int[] p1, Consumer p2){} + public void onFinishTemporaryDetach(){} + public void onGlobalFocusChanged(View p0, View p1){} + public void onPause(){} + public void onProvideAutofillVirtualStructure(ViewStructure p0, int p1){} + public void onProvideContentCaptureStructure(ViewStructure p0, int p1){} + public void onProvideVirtualStructure(ViewStructure p0){} + public void onResume(){} + public void onStartTemporaryDetach(){} + public void onVirtualViewTranslationResponses(LongSparseArray p0){} + public void onWindowFocusChanged(boolean p0){} + public void pauseTimers(){} + public void postUrl(String p0, byte[] p1){} + public void postVisualStateCallback(long p0, WebView.VisualStateCallback p1){} + public void postWebMessage(WebMessage p0, Uri p1){} + public void reload(){} + public void removeJavascriptInterface(String p0){} + public void requestFocusNodeHref(Message p0){} + public void requestImageRef(Message p0){} + public void resumeTimers(){} + public void savePassword(String p0, String p1, String p2){} + public void saveWebArchive(String p0){} + public void saveWebArchive(String p0, boolean p1, ValueCallback p2){} + public void setBackgroundColor(int p0){} + public void setCertificate(SslCertificate p0){} + public void setDownloadListener(DownloadListener p0){} + public void setFindListener(WebView.FindListener p0){} + public void setHorizontalScrollbarOverlay(boolean p0){} + public void setHttpAuthUsernamePassword(String p0, String p1, String p2, String p3){} + public void setInitialScale(int p0){} + public void setLayerType(int p0, Paint p1){} + public void setLayoutParams(ViewGroup.LayoutParams p0){} + public void setMapTrackballToArrowKeys(boolean p0){} + public void setNetworkAvailable(boolean p0){} + public void setOverScrollMode(int p0){} + public void setPictureListener(WebView.PictureListener p0){} + public void setRendererPriorityPolicy(int p0, boolean p1){} + public void setScrollBarStyle(int p0){} + public void setTextClassifier(TextClassifier p0){} + public void setVerticalScrollbarOverlay(boolean p0){} + public void setWebChromeClient(WebChromeClient p0){} + public void setWebViewClient(WebViewClient p0){} + public void setWebViewRenderProcessClient(Executor p0, WebViewRenderProcessClient p1){} + public void setWebViewRenderProcessClient(WebViewRenderProcessClient p0){} + public void stopLoading(){} + public void zoomBy(float p0){} + static public class HitTestResult + { + public String getExtra(){ return null; } + public int getType(){ return 0; } + public static int ANCHOR_TYPE = 0; + public static int EDIT_TEXT_TYPE = 0; + public static int EMAIL_TYPE = 0; + public static int GEO_TYPE = 0; + public static int IMAGE_ANCHOR_TYPE = 0; + public static int IMAGE_TYPE = 0; + public static int PHONE_TYPE = 0; + public static int SRC_ANCHOR_TYPE = 0; + public static int SRC_IMAGE_ANCHOR_TYPE = 0; + public static int UNKNOWN_TYPE = 0; } - - public boolean overlayVerticalScrollbar() { - return false; + static public interface FindListener + { + void onFindResultReceived(int p0, int p1, boolean p2); } - - public void savePassword(String host, String username, String password) {} - - public void setHttpAuthUsernamePassword(String host, String realm, String username, - String password) {} - - public String[] getHttpAuthUsernamePassword(String host, String realm) { - return null; - } - - public void destroy() {} - - public static void enablePlatformNotifications() {} - - public static void disablePlatformNotifications() {} - - public void loadUrl(String url) {} - - public void loadData(String data, String mimeType, String encoding) {} - - public void loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, - String failUrl) {} - - public void stopLoading() {} - - public void reload() {} - - public boolean canGoBack() { - return false; - } - - public void goBack() {} - - public boolean canGoForward() { - return false; - } - - public void goForward() {} - - public boolean canGoBackOrForward(int steps) { - return false; - } - - public void goBackOrForward(int steps) {} - - public boolean pageUp(boolean top) { - return false; - } - - public boolean pageDown(boolean bottom) { - return false; - } - - public void clearView() {} - - public float getScale() { - return 0; - } - - public void setInitialScale(int scaleInPercent) {} - - public void invokeZoomPicker() {} - - public String getUrl() { - return null; - } - - public String getTitle() { - return null; - } - - public int getProgress() { - return 0; - } - - public int getContentHeight() { - return 0; - } - - public void pauseTimers() {} - - public void resumeTimers() {} - - public void clearCache() {} - - public void clearFormData() {} - - public void clearHistory() {} - - public void clearSslPreferences() {} - - public static String findAddress(String addr) { - return null; - } - - public void setWebViewClient(WebViewClient client) {} - - public void addJavascriptInterface(Object obj, String interfaceName) {} - - public View getZoomControls() { - return null; - } - - public boolean zoomIn() { - return false; - } - - public boolean zoomOut() { - return false; - } - - public WebSettings getSettings() { - return null; + static public interface PictureListener + { + void onNewPicture(WebView p0, Picture p1); } } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java index 03a98480210..e63cf85c9c8 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewClient.java @@ -1,231 +1,66 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Generated automatically from android.webkit.WebViewClient for testing purposes + package android.webkit; -public class WebViewClient { - /** - * Give the host application a chance to take over the control when a new url is - * about to be loaded in the current WebView. If WebViewClient is not provided, - * by default WebView will ask Activity Manager to choose the proper handler for - * the url. If WebViewClient is provided, return {@code true} means the host - * application handles the url, while return {@code false} means the current - * WebView handles the url. This method is not called for requests using the - * POST "method". - * - * @param view The WebView that is initiating the callback. - * @param url The url to be loaded. - * @return {@code true} if the host application wants to leave the current - * WebView and handle the url itself, otherwise return {@code false}. - * @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest) - * shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead. - */ - @Deprecated - public boolean shouldOverrideUrlLoading(WebView view, String url) { - return false; - } - - /** - * Give the host application a chance to take over the control when a new url is - * about to be loaded in the current WebView. If WebViewClient is not provided, - * by default WebView will ask Activity Manager to choose the proper handler for - * the url. If WebViewClient is provided, return {@code true} means the host - * application handles the url, while return {@code false} means the current - * WebView handles the url. - * - *

    - * Notes: - *

      - *
    • This method is not called for requests using the POST - * "method".
    • - *
    • This method is also called for subframes with non-http schemes, thus it - * is strongly disadvised to unconditionally call - * {@link WebView#loadUrl(String)} with the request's url from inside the method - * and then return {@code true}, as this will make WebView to attempt loading a - * non-http url, and thus fail.
    • - *
    - * - * @param view The WebView that is initiating the callback. - * @param request Object containing the details of the request. - * @return {@code true} if the host application wants to leave the current - * WebView and handle the url itself, otherwise return {@code false}. - */ - public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { - return false; - } - - /** - * Notify the host application that a page has finished loading. This method is - * called only for main frame. When onPageFinished() is called, the rendering - * picture may not be updated yet. To get the notification for the new Picture, - * use {@link WebView.PictureListener#onNewPicture}. - * - * @param view The WebView that is initiating the callback. - * @param url The url of the page. - */ - public void onPageFinished(WebView view, String url) { - } - - /** - * Notify the host application that the WebView will load the resource specified - * by the given url. - * - * @param view The WebView that is initiating the callback. - * @param url The url of the resource the WebView will load. - */ - public void onLoadResource(WebView view, String url) { - } - - /** - * Notify the host application that {@link android.webkit.WebView} content left - * over from previous page navigations will no longer be drawn. - * - *

    - * This callback can be used to determine the point at which it is safe to make - * a recycled {@link android.webkit.WebView} visible, ensuring that no stale - * content is shown. It is called at the earliest point at which it can be - * guaranteed that {@link WebView#onDraw} will no longer draw any content from - * previous navigations. The next draw will display either the - * {@link WebView#setBackgroundColor background color} of the {@link WebView}, - * or some of the contents of the newly loaded page. - * - *

    - * This method is called when the body of the HTTP response has started loading, - * is reflected in the DOM, and will be visible in subsequent draws. This - * callback occurs early in the document loading process, and as such you should - * expect that linked resources (for example, CSS and images) may not be - * available. - * - *

    - * For more fine-grained notification of visual state updates, see - * {@link WebView#postVisualStateCallback}. - * - *

    - * Please note that all the conditions and recommendations applicable to - * {@link WebView#postVisualStateCallback} also apply to this API. - * - *

    - * This callback is only called for main frame navigations. - * - * @param view The {@link android.webkit.WebView} for which the navigation - * occurred. - * @param url The URL corresponding to the page navigation that triggered this - * callback. - */ - public void onPageCommitVisible(WebView view, String url) { - } - - /** - * Notify the host application of a resource request and allow the application - * to return the data. If the return value is {@code null}, the WebView will - * continue to load the resource as usual. Otherwise, the return response and - * data will be used. - * - *

    - * This callback is invoked for a variety of URL schemes (e.g., - * {@code http(s):}, {@code - * data:}, {@code file:}, etc.), not only those schemes which send requests over - * the network. This is not called for {@code javascript:} URLs, {@code blob:} - * URLs, or for assets accessed via {@code file:///android_asset/} or - * {@code file:///android_res/} URLs. - * - *

    - * In the case of redirects, this is only called for the initial resource URL, - * not any subsequent redirect URLs. - * - *

    - * Note: This method is called on a thread other than the UI thread so - * clients should exercise caution when accessing private data or the view - * system. - * - *

    - * Note: When Safe Browsing is enabled, these URLs still undergo Safe - * Browsing checks. If this is undesired, whitelist the URL with - * {@link WebView#setSafeBrowsingWhitelist} or ignore the warning with - * {@link #onSafeBrowsingHit}. - * - * @param view The {@link android.webkit.WebView} that is requesting the - * resource. - * @param url The raw url of the resource. - * @return A {@link android.webkit.WebResourceResponse} containing the response - * information or {@code null} if the WebView should load the resource - * itself. - * @deprecated Use {@link #shouldInterceptRequest(WebView, WebResourceRequest) - * shouldInterceptRequest(WebView, WebResourceRequest)} instead. - */ - @Deprecated - public WebResourceResponse shouldInterceptRequest(WebView view, String url) { - return null; - } - - /** - * Notify the host application of a resource request and allow the application - * to return the data. If the return value is {@code null}, the WebView will - * continue to load the resource as usual. Otherwise, the return response and - * data will be used. - * - *

    - * This callback is invoked for a variety of URL schemes (e.g., - * {@code http(s):}, {@code - * data:}, {@code file:}, etc.), not only those schemes which send requests over - * the network. This is not called for {@code javascript:} URLs, {@code blob:} - * URLs, or for assets accessed via {@code file:///android_asset/} or - * {@code file:///android_res/} URLs. - * - *

    - * In the case of redirects, this is only called for the initial resource URL, - * not any subsequent redirect URLs. - * - *

    - * Note: This method is called on a thread other than the UI thread so - * clients should exercise caution when accessing private data or the view - * system. - * - *

    - * Note: When Safe Browsing is enabled, these URLs still undergo Safe - * Browsing checks. If this is undesired, whitelist the URL with - * {@link WebView#setSafeBrowsingWhitelist} or ignore the warning with - * {@link #onSafeBrowsingHit}. - * - * @param view The {@link android.webkit.WebView} that is requesting the - * resource. - * @param request Object containing the details of the request. - * @return A {@link android.webkit.WebResourceResponse} containing the response - * information or {@code null} if the WebView should load the resource - * itself. - */ - public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { - return null; - } - - /** - * Report an error to the host application. These errors are unrecoverable (i.e. - * the main resource is unavailable). The {@code errorCode} parameter - * corresponds to one of the {@code ERROR_*} constants. - * - * @param view The WebView that is initiating the callback. - * @param errorCode The error code corresponding to an ERROR_* value. - * @param description A String describing the error. - * @param failingUrl The url that failed to load. - * @deprecated Use - * {@link #onReceivedError(WebView, WebResourceRequest, WebResourceError) - * onReceivedError(WebView, WebResourceRequest, WebResourceError)} - * instead. - */ - @Deprecated - public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { - } +import android.graphics.Bitmap; +import android.net.http.SslError; +import android.os.Message; +import android.view.KeyEvent; +import android.webkit.ClientCertRequest; +import android.webkit.HttpAuthHandler; +import android.webkit.RenderProcessGoneDetail; +import android.webkit.SafeBrowsingResponse; +import android.webkit.SslErrorHandler; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebView; +public class WebViewClient +{ + public WebResourceResponse shouldInterceptRequest(WebView p0, String p1){ return null; } + public WebResourceResponse shouldInterceptRequest(WebView p0, WebResourceRequest p1){ return null; } + public WebViewClient(){} + public boolean onRenderProcessGone(WebView p0, RenderProcessGoneDetail p1){ return false; } + public boolean shouldOverrideKeyEvent(WebView p0, KeyEvent p1){ return false; } + public boolean shouldOverrideUrlLoading(WebView p0, String p1){ return false; } + public boolean shouldOverrideUrlLoading(WebView p0, WebResourceRequest p1){ return false; } + public static int ERROR_AUTHENTICATION = 0; + public static int ERROR_BAD_URL = 0; + public static int ERROR_CONNECT = 0; + public static int ERROR_FAILED_SSL_HANDSHAKE = 0; + public static int ERROR_FILE = 0; + public static int ERROR_FILE_NOT_FOUND = 0; + public static int ERROR_HOST_LOOKUP = 0; + public static int ERROR_IO = 0; + public static int ERROR_PROXY_AUTHENTICATION = 0; + public static int ERROR_REDIRECT_LOOP = 0; + public static int ERROR_TIMEOUT = 0; + public static int ERROR_TOO_MANY_REQUESTS = 0; + public static int ERROR_UNKNOWN = 0; + public static int ERROR_UNSAFE_RESOURCE = 0; + public static int ERROR_UNSUPPORTED_AUTH_SCHEME = 0; + public static int ERROR_UNSUPPORTED_SCHEME = 0; + public static int SAFE_BROWSING_THREAT_BILLING = 0; + public static int SAFE_BROWSING_THREAT_MALWARE = 0; + public static int SAFE_BROWSING_THREAT_PHISHING = 0; + public static int SAFE_BROWSING_THREAT_UNKNOWN = 0; + public static int SAFE_BROWSING_THREAT_UNWANTED_SOFTWARE = 0; + public void doUpdateVisitedHistory(WebView p0, String p1, boolean p2){} + public void onFormResubmission(WebView p0, Message p1, Message p2){} + public void onLoadResource(WebView p0, String p1){} + public void onPageCommitVisible(WebView p0, String p1){} + public void onPageFinished(WebView p0, String p1){} + public void onPageStarted(WebView p0, String p1, Bitmap p2){} + public void onReceivedClientCertRequest(WebView p0, ClientCertRequest p1){} + public void onReceivedError(WebView p0, WebResourceRequest p1, WebResourceError p2){} + public void onReceivedError(WebView p0, int p1, String p2, String p3){} + public void onReceivedHttpAuthRequest(WebView p0, HttpAuthHandler p1, String p2, String p3){} + public void onReceivedHttpError(WebView p0, WebResourceRequest p1, WebResourceResponse p2){} + public void onReceivedLoginRequest(WebView p0, String p1, String p2, String p3){} + public void onReceivedSslError(WebView p0, SslErrorHandler p1, SslError p2){} + public void onSafeBrowsingHit(WebView p0, WebResourceRequest p1, int p2, SafeBrowsingResponse p3){} + public void onScaleChanged(WebView p0, float p1, float p2){} + public void onTooManyRedirects(WebView p0, Message p1, Message p2){} + public void onUnhandledKeyEvent(WebView p0, KeyEvent p1){} } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcess.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcess.java new file mode 100644 index 00000000000..0c9d0d1385c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcess.java @@ -0,0 +1,10 @@ +// Generated automatically from android.webkit.WebViewRenderProcess for testing purposes + +package android.webkit; + + +abstract public class WebViewRenderProcess +{ + public WebViewRenderProcess(){} + public abstract boolean terminate(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcessClient.java b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcessClient.java new file mode 100644 index 00000000000..742a6ed1438 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/webkit/WebViewRenderProcessClient.java @@ -0,0 +1,13 @@ +// Generated automatically from android.webkit.WebViewRenderProcessClient for testing purposes + +package android.webkit; + +import android.webkit.WebView; +import android.webkit.WebViewRenderProcess; + +abstract public class WebViewRenderProcessClient +{ + public WebViewRenderProcessClient(){} + public abstract void onRenderProcessResponsive(WebView p0, WebViewRenderProcess p1); + public abstract void onRenderProcessUnresponsive(WebView p0, WebViewRenderProcess p1); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsListView.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsListView.java new file mode 100644 index 00000000000..64da209cc56 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsListView.java @@ -0,0 +1,212 @@ +// Generated automatically from android.widget.AbsListView for testing purposes + +package android.widget; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Parcelable; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.util.SparseBooleanArray; +import android.view.ActionMode; +import android.view.ContextMenu; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.widget.Adapter; +import android.widget.AdapterView; +import android.widget.Filter; +import android.widget.ListAdapter; +import java.util.ArrayList; +import java.util.List; + +abstract public class AbsListView extends AdapterView implements Filter.FilterListener, TextWatcher, ViewTreeObserver.OnGlobalLayoutListener, ViewTreeObserver.OnTouchModeChangeListener +{ + protected AbsListView() {} + protected ContextMenu.ContextMenuInfo getContextMenuInfo(){ return null; } + protected ViewGroup.LayoutParams generateDefaultLayoutParams(){ return null; } + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p0){ return null; } + protected boolean checkLayoutParams(ViewGroup.LayoutParams p0){ return false; } + protected boolean isInFilterMode(){ return false; } + protected boolean isPaddingOffsetRequired(){ return false; } + protected float getBottomFadingEdgeStrength(){ return 0; } + protected float getTopFadingEdgeStrength(){ return 0; } + protected int computeVerticalScrollExtent(){ return 0; } + protected int computeVerticalScrollOffset(){ return 0; } + protected int computeVerticalScrollRange(){ return 0; } + protected int getBottomPaddingOffset(){ return 0; } + protected int getLeftPaddingOffset(){ return 0; } + protected int getRightPaddingOffset(){ return 0; } + protected int getTopPaddingOffset(){ return 0; } + protected void dispatchDraw(Canvas p0){} + protected void dispatchSetPressed(boolean p0){} + protected void drawableStateChanged(){} + protected void handleDataChanged(){} + protected void layoutChildren(){} + protected void onAttachedToWindow(){} + protected void onDetachedFromWindow(){} + protected void onDisplayHint(int p0){} + protected void onFocusChanged(boolean p0, int p1, Rect p2){} + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + protected void onMeasure(int p0, int p1){} + protected void onOverScrolled(int p0, int p1, boolean p2, boolean p3){} + protected void onSizeChanged(int p0, int p1, int p2, int p3){} + public AbsListView(Context p0){} + public AbsListView(Context p0, AttributeSet p1){} + public AbsListView(Context p0, AttributeSet p1, int p2){} + public AbsListView(Context p0, AttributeSet p1, int p2, int p3){} + public AbsListView.LayoutParams generateLayoutParams(AttributeSet p0){ return null; } + public CharSequence getAccessibilityClassName(){ return null; } + public CharSequence getTextFilter(){ return null; } + public Drawable getSelector(){ return null; } + public InputConnection onCreateInputConnection(EditorInfo p0){ return null; } + public Parcelable onSaveInstanceState(){ return null; } + public PointerIcon onResolvePointerIcon(MotionEvent p0, int p1){ return null; } + public SparseBooleanArray getCheckedItemPositions(){ return null; } + public View getSelectedView(){ return null; } + public boolean canScrollList(int p0){ return false; } + public boolean checkInputConnectionProxy(View p0){ return false; } + public boolean hasTextFilter(){ return false; } + public boolean isDrawSelectorOnTop(){ return false; } + public boolean isFastScrollAlwaysVisible(){ return false; } + public boolean isFastScrollEnabled(){ return false; } + public boolean isItemChecked(int p0){ return false; } + public boolean isScrollingCacheEnabled(){ return false; } + public boolean isSmoothScrollbarEnabled(){ return false; } + public boolean isStackFromBottom(){ return false; } + public boolean isTextFilterEnabled(){ return false; } + public boolean onGenericMotionEvent(MotionEvent p0){ return false; } + public boolean onInterceptHoverEvent(MotionEvent p0){ return false; } + public boolean onInterceptTouchEvent(MotionEvent p0){ return false; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public boolean onNestedFling(View p0, float p1, float p2, boolean p3){ return false; } + public boolean onRemoteAdapterConnected(){ return false; } + public boolean onStartNestedScroll(View p0, View p1, int p2){ return false; } + public boolean onTouchEvent(MotionEvent p0){ return false; } + public boolean performItemClick(View p0, int p1, long p2){ return false; } + public boolean showContextMenu(){ return false; } + public boolean showContextMenu(float p0, float p1){ return false; } + public boolean showContextMenuForChild(View p0){ return false; } + public boolean showContextMenuForChild(View p0, float p1, float p2){ return false; } + public boolean verifyDrawable(Drawable p0){ return false; } + public int getBottomEdgeEffectColor(){ return 0; } + public int getCacheColorHint(){ return 0; } + public int getCheckedItemCount(){ return 0; } + public int getCheckedItemPosition(){ return 0; } + public int getChoiceMode(){ return 0; } + public int getListPaddingBottom(){ return 0; } + public int getListPaddingLeft(){ return 0; } + public int getListPaddingRight(){ return 0; } + public int getListPaddingTop(){ return 0; } + public int getSolidColor(){ return 0; } + public int getTopEdgeEffectColor(){ return 0; } + public int getTranscriptMode(){ return 0; } + public int getVerticalScrollbarWidth(){ return 0; } + public int pointToPosition(int p0, int p1){ return 0; } + public long pointToRowId(int p0, int p1){ return 0; } + public long[] getCheckedItemIds(){ return null; } + public static int CHOICE_MODE_MULTIPLE = 0; + public static int CHOICE_MODE_MULTIPLE_MODAL = 0; + public static int CHOICE_MODE_NONE = 0; + public static int CHOICE_MODE_SINGLE = 0; + public static int TRANSCRIPT_MODE_ALWAYS_SCROLL = 0; + public static int TRANSCRIPT_MODE_DISABLED = 0; + public static int TRANSCRIPT_MODE_NORMAL = 0; + public void addTouchables(ArrayList p0){} + public void afterTextChanged(Editable p0){} + public void beforeTextChanged(CharSequence p0, int p1, int p2, int p3){} + public void clearChoices(){} + public void clearTextFilter(){} + public void deferNotifyDataSetChanged(){} + public void dispatchDrawableHotspotChanged(float p0, float p1){} + public void draw(Canvas p0){} + public void fling(int p0){} + public void getFocusedRect(Rect p0){} + public void invalidateViews(){} + public void jumpDrawablesToCurrentState(){} + public void onCancelPendingInputEvents(){} + public void onFilterComplete(int p0){} + public void onGlobalLayout(){} + public void onInitializeAccessibilityNodeInfoForItem(View p0, int p1, AccessibilityNodeInfo p2){} + public void onNestedScroll(View p0, int p1, int p2, int p3, int p4){} + public void onNestedScrollAccepted(View p0, View p1, int p2){} + public void onRemoteAdapterDisconnected(){} + public void onRestoreInstanceState(Parcelable p0){} + public void onRtlPropertiesChanged(int p0){} + public void onTextChanged(CharSequence p0, int p1, int p2, int p3){} + public void onTouchModeChanged(boolean p0){} + public void onWindowFocusChanged(boolean p0){} + public void reclaimViews(List p0){} + public void requestDisallowInterceptTouchEvent(boolean p0){} + public void requestLayout(){} + public void scrollListBy(int p0){} + public void setAdapter(ListAdapter p0){} + public void setBottomEdgeEffectColor(int p0){} + public void setCacheColorHint(int p0){} + public void setChoiceMode(int p0){} + public void setDrawSelectorOnTop(boolean p0){} + public void setEdgeEffectColor(int p0){} + public void setFastScrollAlwaysVisible(boolean p0){} + public void setFastScrollEnabled(boolean p0){} + public void setFastScrollStyle(int p0){} + public void setFilterText(String p0){} + public void setFriction(float p0){} + public void setItemChecked(int p0, boolean p1){} + public void setMultiChoiceModeListener(AbsListView.MultiChoiceModeListener p0){} + public void setOnScrollListener(AbsListView.OnScrollListener p0){} + public void setRecyclerListener(AbsListView.RecyclerListener p0){} + public void setRemoteViewsAdapter(Intent p0){} + public void setScrollBarStyle(int p0){} + public void setScrollIndicators(View p0, View p1){} + public void setScrollingCacheEnabled(boolean p0){} + public void setSelectionFromTop(int p0, int p1){} + public void setSelector(Drawable p0){} + public void setSelector(int p0){} + public void setSmoothScrollbarEnabled(boolean p0){} + public void setStackFromBottom(boolean p0){} + public void setTextFilterEnabled(boolean p0){} + public void setTopEdgeEffectColor(int p0){} + public void setTranscriptMode(int p0){} + public void setVelocityScale(float p0){} + public void setVerticalScrollbarPosition(int p0){} + public void smoothScrollBy(int p0, int p1){} + public void smoothScrollToPosition(int p0){} + public void smoothScrollToPosition(int p0, int p1){} + public void smoothScrollToPositionFromTop(int p0, int p1){} + public void smoothScrollToPositionFromTop(int p0, int p1, int p2){} + static public class LayoutParams extends ViewGroup.LayoutParams + { + protected LayoutParams() {} + public LayoutParams(Context p0, AttributeSet p1){} + public LayoutParams(ViewGroup.LayoutParams p0){} + public LayoutParams(int p0, int p1){} + public LayoutParams(int p0, int p1, int p2){} + } + static public interface MultiChoiceModeListener extends ActionMode.Callback + { + void onItemCheckedStateChanged(ActionMode p0, int p1, long p2, boolean p3); + } + static public interface OnScrollListener + { + static int SCROLL_STATE_FLING = 0; + static int SCROLL_STATE_IDLE = 0; + static int SCROLL_STATE_TOUCH_SCROLL = 0; + void onScroll(AbsListView p0, int p1, int p2, int p3); + void onScrollStateChanged(AbsListView p0, int p1); + } + static public interface RecyclerListener + { + void onMovedToScrapHeap(View p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsoluteLayout.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsoluteLayout.java new file mode 100644 index 00000000000..438d8feca86 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/AbsoluteLayout.java @@ -0,0 +1,23 @@ +// Generated automatically from android.widget.AbsoluteLayout for testing purposes + +package android.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.ViewGroup; + +public class AbsoluteLayout extends ViewGroup +{ + protected AbsoluteLayout() {} + protected ViewGroup.LayoutParams generateDefaultLayoutParams(){ return null; } + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p0){ return null; } + protected boolean checkLayoutParams(ViewGroup.LayoutParams p0){ return false; } + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + protected void onMeasure(int p0, int p1){} + public AbsoluteLayout(Context p0){} + public AbsoluteLayout(Context p0, AttributeSet p1){} + public AbsoluteLayout(Context p0, AttributeSet p1, int p2){} + public AbsoluteLayout(Context p0, AttributeSet p1, int p2, int p3){} + public ViewGroup.LayoutParams generateLayoutParams(AttributeSet p0){ return null; } + public boolean shouldDelayChildPressedState(){ return false; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/Adapter.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/Adapter.java new file mode 100644 index 00000000000..06f719feab0 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/Adapter.java @@ -0,0 +1,24 @@ +// Generated automatically from android.widget.Adapter for testing purposes + +package android.widget; + +import android.database.DataSetObserver; +import android.view.View; +import android.view.ViewGroup; + +public interface Adapter +{ + Object getItem(int p0); + View getView(int p0, View p1, ViewGroup p2); + boolean hasStableIds(); + boolean isEmpty(); + default CharSequence[] getAutofillOptions(){ return null; } + int getCount(); + int getItemViewType(int p0); + int getViewTypeCount(); + long getItemId(int p0); + static int IGNORE_ITEM_VIEW_TYPE = 0; + static int NO_SELECTION = 0; + void registerDataSetObserver(DataSetObserver p0); + void unregisterDataSetObserver(DataSetObserver p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/AdapterView.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/AdapterView.java new file mode 100644 index 00000000000..a071941461c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/AdapterView.java @@ -0,0 +1,77 @@ +// Generated automatically from android.widget.AdapterView for testing purposes + +package android.widget; + +import android.content.Context; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewStructure; +import android.widget.Adapter; + +abstract public class AdapterView extends ViewGroup +{ + protected AdapterView() {} + protected boolean canAnimate(){ return false; } + protected void dispatchRestoreInstanceState(SparseArray p0){} + protected void dispatchSaveInstanceState(SparseArray p0){} + protected void onDetachedFromWindow(){} + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + public AdapterView(Context p0){} + public AdapterView(Context p0, AttributeSet p1){} + public AdapterView(Context p0, AttributeSet p1, int p2){} + public AdapterView(Context p0, AttributeSet p1, int p2, int p3){} + public CharSequence getAccessibilityClassName(){ return null; } + public Object getItemAtPosition(int p0){ return null; } + public Object getSelectedItem(){ return null; } + public View getEmptyView(){ return null; } + public abstract T getAdapter(); + public abstract View getSelectedView(); + public abstract void setAdapter(T p0); + public abstract void setSelection(int p0); + public boolean performItemClick(View p0, int p1, long p2){ return false; } + public final AdapterView.OnItemClickListener getOnItemClickListener(){ return null; } + public final AdapterView.OnItemLongClickListener getOnItemLongClickListener(){ return null; } + public final AdapterView.OnItemSelectedListener getOnItemSelectedListener(){ return null; } + public int getCount(){ return 0; } + public int getFirstVisiblePosition(){ return 0; } + public int getLastVisiblePosition(){ return 0; } + public int getPositionForView(View p0){ return 0; } + public int getSelectedItemPosition(){ return 0; } + public long getItemIdAtPosition(int p0){ return 0; } + public long getSelectedItemId(){ return 0; } + public static int INVALID_POSITION = 0; + public static int ITEM_VIEW_TYPE_HEADER_OR_FOOTER = 0; + public static int ITEM_VIEW_TYPE_IGNORE = 0; + public static long INVALID_ROW_ID = 0; + public void addView(View p0){} + public void addView(View p0, ViewGroup.LayoutParams p1){} + public void addView(View p0, int p1){} + public void addView(View p0, int p1, ViewGroup.LayoutParams p2){} + public void onProvideAutofillStructure(ViewStructure p0, int p1){} + public void removeAllViews(){} + public void removeView(View p0){} + public void removeViewAt(int p0){} + public void setEmptyView(View p0){} + public void setFocusable(int p0){} + public void setFocusableInTouchMode(boolean p0){} + public void setOnClickListener(View.OnClickListener p0){} + public void setOnItemClickListener(AdapterView.OnItemClickListener p0){} + public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener p0){} + public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener p0){} + static public interface OnItemClickListener + { + void onItemClick(AdapterView p0, View p1, int p2, long p3); + } + static public interface OnItemLongClickListener + { + boolean onItemLongClick(AdapterView p0, View p1, int p2, long p3); + } + static public interface OnItemSelectedListener + { + void onItemSelected(AdapterView p0, View p1, int p2, long p3); + void onNothingSelected(AdapterView p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/Button.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/Button.java index 69cf1911ffd..53413c878b4 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/widget/Button.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/Button.java @@ -1,42 +1,20 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ +// Generated automatically from android.widget.Button for testing purposes package android.widget; import android.content.Context; import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.PointerIcon; +import android.widget.TextView; -public class Button extends TextView { - public Button(Context context) { - super(null); - } - - public Button(Context context, AttributeSet attrs) { - super(null); - } - - public Button(Context context, AttributeSet attrs, int defStyleAttr) { - super(null); - } - - public Button(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(null); - } - - @Override - public CharSequence getAccessibilityClassName() { - return null; - } - +public class Button extends TextView +{ + protected Button() {} + public Button(Context p0){} + public Button(Context p0, AttributeSet p1){} + public Button(Context p0, AttributeSet p1, int p2){} + public Button(Context p0, AttributeSet p1, int p2, int p3){} + public CharSequence getAccessibilityClassName(){ return null; } + public PointerIcon onResolvePointerIcon(MotionEvent p0, int p1){ return null; } } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/Filter.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/Filter.java new file mode 100644 index 00000000000..f0572f12d78 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/Filter.java @@ -0,0 +1,24 @@ +// Generated automatically from android.widget.Filter for testing purposes + +package android.widget; + + +abstract public class Filter +{ + protected abstract Filter.FilterResults performFiltering(CharSequence p0); + protected abstract void publishResults(CharSequence p0, Filter.FilterResults p1); + public CharSequence convertResultToString(Object p0){ return null; } + public Filter(){} + public final void filter(CharSequence p0){} + public final void filter(CharSequence p0, Filter.FilterListener p1){} + static class FilterResults + { + public FilterResults(){} + public Object values = null; + public int count = 0; + } + static public interface FilterListener + { + void onFilterComplete(int p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/FrameLayout.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/FrameLayout.java new file mode 100644 index 00000000000..6901b838087 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/FrameLayout.java @@ -0,0 +1,40 @@ +// Generated automatically from android.widget.FrameLayout for testing purposes + +package android.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.ViewGroup; + +public class FrameLayout extends ViewGroup +{ + protected FrameLayout() {} + protected FrameLayout.LayoutParams generateDefaultLayoutParams(){ return null; } + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p0){ return null; } + protected boolean checkLayoutParams(ViewGroup.LayoutParams p0){ return false; } + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + protected void onMeasure(int p0, int p1){} + public CharSequence getAccessibilityClassName(){ return null; } + public FrameLayout(Context p0){} + public FrameLayout(Context p0, AttributeSet p1){} + public FrameLayout(Context p0, AttributeSet p1, int p2){} + public FrameLayout(Context p0, AttributeSet p1, int p2, int p3){} + public FrameLayout.LayoutParams generateLayoutParams(AttributeSet p0){ return null; } + public boolean getConsiderGoneChildrenWhenMeasuring(){ return false; } + public boolean getMeasureAllChildren(){ return false; } + public boolean shouldDelayChildPressedState(){ return false; } + public void setForegroundGravity(int p0){} + public void setMeasureAllChildren(boolean p0){} + static public class LayoutParams extends ViewGroup.MarginLayoutParams + { + protected LayoutParams() {} + public LayoutParams(Context p0, AttributeSet p1){} + public LayoutParams(FrameLayout.LayoutParams p0){} + public LayoutParams(ViewGroup.LayoutParams p0){} + public LayoutParams(ViewGroup.MarginLayoutParams p0){} + public LayoutParams(int p0, int p1){} + public LayoutParams(int p0, int p1, int p2){} + public int gravity = 0; + public static int UNSPECIFIED_GRAVITY = 0; + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/ListAdapter.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/ListAdapter.java new file mode 100644 index 00000000000..c23fea65b27 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/ListAdapter.java @@ -0,0 +1,11 @@ +// Generated automatically from android.widget.ListAdapter for testing purposes + +package android.widget; + +import android.widget.Adapter; + +public interface ListAdapter extends Adapter +{ + boolean areAllItemsEnabled(); + boolean isEnabled(int p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/ListView.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/ListView.java new file mode 100644 index 00000000000..088efbc883f --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/ListView.java @@ -0,0 +1,73 @@ +// Generated automatically from android.widget.ListView for testing purposes + +package android.widget; + +import android.content.Context; +import android.content.Intent; +import android.graphics.Canvas; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.AbsListView; +import android.widget.ListAdapter; + +public class ListView extends AbsListView +{ + protected ListView() {} + protected boolean canAnimate(){ return false; } + protected boolean drawChild(Canvas p0, View p1, long p2){ return false; } + protected void dispatchDraw(Canvas p0){} + protected void layoutChildren(){} + protected void onDetachedFromWindow(){} + protected void onFinishInflate(){} + protected void onFocusChanged(boolean p0, int p1, Rect p2){} + protected void onMeasure(int p0, int p1){} + protected void onSizeChanged(int p0, int p1, int p2, int p3){} + public CharSequence getAccessibilityClassName(){ return null; } + public Drawable getDivider(){ return null; } + public Drawable getOverscrollFooter(){ return null; } + public Drawable getOverscrollHeader(){ return null; } + public ListAdapter getAdapter(){ return null; } + public ListView(Context p0){} + public ListView(Context p0, AttributeSet p1){} + public ListView(Context p0, AttributeSet p1, int p2){} + public ListView(Context p0, AttributeSet p1, int p2, int p3){} + public boolean areFooterDividersEnabled(){ return false; } + public boolean areHeaderDividersEnabled(){ return false; } + public boolean dispatchKeyEvent(KeyEvent p0){ return false; } + public boolean getItemsCanFocus(){ return false; } + public boolean isOpaque(){ return false; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyMultiple(int p0, int p1, KeyEvent p2){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public boolean removeFooterView(View p0){ return false; } + public boolean removeHeaderView(View p0){ return false; } + public boolean requestChildRectangleOnScreen(View p0, Rect p1, boolean p2){ return false; } + public int getDividerHeight(){ return 0; } + public int getFooterViewsCount(){ return 0; } + public int getHeaderViewsCount(){ return 0; } + public int getMaxScrollAmount(){ return 0; } + public long[] getCheckItemIds(){ return null; } + public void addFooterView(View p0){} + public void addFooterView(View p0, Object p1, boolean p2){} + public void addHeaderView(View p0){} + public void addHeaderView(View p0, Object p1, boolean p2){} + public void onInitializeAccessibilityNodeInfoForItem(View p0, int p1, AccessibilityNodeInfo p2){} + public void setAdapter(ListAdapter p0){} + public void setCacheColorHint(int p0){} + public void setDivider(Drawable p0){} + public void setDividerHeight(int p0){} + public void setFooterDividersEnabled(boolean p0){} + public void setHeaderDividersEnabled(boolean p0){} + public void setItemsCanFocus(boolean p0){} + public void setOverscrollFooter(Drawable p0){} + public void setOverscrollHeader(Drawable p0){} + public void setRemoteViewsAdapter(Intent p0){} + public void setSelection(int p0){} + public void setSelectionAfterHeaderView(){} + public void smoothScrollByOffset(int p0){} + public void smoothScrollToPosition(int p0){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/SpinnerAdapter.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/SpinnerAdapter.java new file mode 100644 index 00000000000..9a389615336 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/SpinnerAdapter.java @@ -0,0 +1,12 @@ +// Generated automatically from android.widget.SpinnerAdapter for testing purposes + +package android.widget; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.Adapter; + +public interface SpinnerAdapter extends Adapter +{ + View getDropDownView(int p0, View p1, ViewGroup p2); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/TextView.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/TextView.java index 7ff5e1f7143..491cf47514a 100644 --- a/java/ql/test/stubs/google-android-9.0.0/android/widget/TextView.java +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/TextView.java @@ -1,816 +1,383 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ +// Generated automatically from android.widget.TextView for testing purposes package android.widget; -import java.util.ArrayList; -import java.util.Locale; -import android.annotation.Nullable; import android.content.Context; import android.content.res.ColorStateList; -import android.content.res.TypedArray; +import android.content.res.Configuration; import android.graphics.BlendMode; +import android.graphics.Canvas; import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.LocaleList; -import android.os.UserHandle; +import android.os.Parcelable; +import android.text.Editable; +import android.text.InputFilter; +import android.text.Layout; +import android.text.PrecomputedText; +import android.text.Spannable; +import android.text.TextDirectionHeuristic; +import android.text.TextPaint; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.KeyListener; +import android.text.method.MovementMethod; +import android.text.method.TransformationMethod; +import android.text.style.URLSpan; import android.util.AttributeSet; +import android.view.ActionMode; +import android.view.ContentInfo; +import android.view.ContextMenu; +import android.view.DragEvent; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.PointerIcon; import android.view.View; - -public class TextView extends View { - public @interface XMLTypefaceAttr { - - } - public @interface AutoSizeTextType { - } - - public static void preloadFontCache() {} - - public TextView(Context context) { - super(null); - } - - public TextView(Context context, AttributeSet attrs) { - super(null); - } - - public TextView(Context context, AttributeSet attrs, int defStyleAttr) { - super(null); - } - - public TextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(null); - } - - public void setAutoSizeTextTypeWithDefaults(int autoSizeTextType) {} - - public void setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize, - int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit) {} - - public void setAutoSizeTextTypeUniformWithPresetSizes(int[] presetSizes, int unit) {} - - public int getAutoSizeTextType() { - return 0; - } - - public int getAutoSizeStepGranularity() { - return 0; - } - - public int getAutoSizeMinTextSize() { - return 0; - } - - public int getAutoSizeMaxTextSize() { - return 0; - } - - public int[] getAutoSizeTextAvailableSizes() { - return null; - } - - public void setTypeface(Typeface tf, int style) {} - - public CharSequence getText() { - return null; - } - - public int length() { - return 0; - } - - public CharSequence getTransformed() { - return null; - } - - public int getLineHeight() { - return 0; - } - - public int getCompoundPaddingTop() { - return 0; - } - - public int getCompoundPaddingBottom() { - return 0; - } - - public int getCompoundPaddingLeft() { - return 0; - } - - public int getCompoundPaddingRight() { - return 0; - } - - public int getCompoundPaddingStart() { - return 0; - } - - public int getCompoundPaddingEnd() { - return 0; - } - - public int getExtendedPaddingTop() { - return 0; - } - - public int getExtendedPaddingBottom() { - return 0; - } - - public int getTotalPaddingLeft() { - return 0; - } - - public int getTotalPaddingRight() { - return 0; - } - - public int getTotalPaddingStart() { - return 0; - } - - public int getTotalPaddingEnd() { - return 0; - } - - public int getTotalPaddingTop() { - return 0; - } - - public int getTotalPaddingBottom() { - return 0; - } - - public void setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom) {} - - public void setCompoundDrawablesWithIntrinsicBounds(int left, int top, int right, int bottom) {} - - public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, - Drawable bottom) {} - - public void setCompoundDrawablesRelative(Drawable start, Drawable top, Drawable end, - Drawable bottom) {} - - public void setCompoundDrawablesRelativeWithIntrinsicBounds(int start, int top, int end, - int bottom) {} - - public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable start, Drawable top, - Drawable end, Drawable bottom) {} - - public Drawable[] getCompoundDrawables() { - return null; - } - - public Drawable[] getCompoundDrawablesRelative() { - return null; - } - - public void setCompoundDrawablePadding(int pad) {} - - public int getCompoundDrawablePadding() { - return 0; - } - - public void setCompoundDrawableTintList(ColorStateList tint) {} - - public ColorStateList getCompoundDrawableTintList() { - return null; - } - - public void setCompoundDrawableTintMode(PorterDuff.Mode tintMode) {} - - public void setCompoundDrawableTintBlendMode(BlendMode blendMode) {} - - public PorterDuff.Mode getCompoundDrawableTintMode() { - return null; - } - - public BlendMode getCompoundDrawableTintBlendMode() { - return null; - } - - public void setFirstBaselineToTopHeight(int firstBaselineToTopHeight) {} - - public void setLastBaselineToBottomHeight(int lastBaselineToBottomHeight) {} - - public int getFirstBaselineToTopHeight() { - return 0; - } - - public int getLastBaselineToBottomHeight() { - return 0; - } - - public final int getAutoLinkMask() { - return 0; - } - - public void setTextSelectHandle(Drawable textSelectHandle) {} - - public void setTextSelectHandle(int textSelectHandle) {} - - @Nullable - public Drawable getTextSelectHandle() { - return null; - } - - public void setTextSelectHandleLeft(Drawable textSelectHandleLeft) {} - - public void setTextSelectHandleLeft(int textSelectHandleLeft) {} - - @Nullable - public Drawable getTextSelectHandleLeft() { - return null; - } - - public void setTextSelectHandleRight(Drawable textSelectHandleRight) {} - - public void setTextSelectHandleRight(int textSelectHandleRight) {} - - @Nullable - public Drawable getTextSelectHandleRight() { - return null; - } - - public void setTextCursorDrawable(Drawable textCursorDrawable) {} - - public void setTextCursorDrawable(int textCursorDrawable) {} - - @Nullable - public Drawable getTextCursorDrawable() { - return null; - } - - public void setTextAppearance(int resId) {} - - public void setTextAppearance(Context context, int resId) {} - - public Locale getTextLocale() { - return null; - } - - public LocaleList getTextLocales() { - return null; - } - - public void setTextLocale(Locale locale) {} - - public void setTextLocales(LocaleList locales) {} - - public float getTextSize() { - return 0; - } - - public float getScaledTextSize() { - return 0; - } - - public int getTypefaceStyle() { - return 0; - } - - public void setTextSize(float size) {} - - public void setTextSize(int unit, float size) {} - - public int getTextSizeUnit() { - return 0; - } - - public float getTextScaleX() { - return 0; - } - - public void setTextScaleX(float size) {} - - public void setTypeface(Typeface tf) {} - - public Typeface getTypeface() { - return null; - } - - public void setElegantTextHeight(boolean elegant) {} - - public void setFallbackLineSpacing(boolean enabled) {} - - public boolean isFallbackLineSpacing() { - return false; - } - - public boolean isElegantTextHeight() { - return false; - } - - public float getLetterSpacing() { - return 0; - } - - public void setLetterSpacing(float letterSpacing) {} - - public String getFontFeatureSettings() { - return null; - } - - public String getFontVariationSettings() { - return null; - } - - public void setBreakStrategy(int breakStrategy) {} - - public int getBreakStrategy() { - return 0; - } - - public void setHyphenationFrequency(int hyphenationFrequency) {} - - public int getHyphenationFrequency() { - return 0; - } - - public void setJustificationMode(int justificationMode) {} - - public int getJustificationMode() { - return 0; - } - - public void setFontFeatureSettings(String fontFeatureSettings) {} - - public boolean setFontVariationSettings(String fontVariationSettings) { - return false; - } - - public void setTextColor(int color) {} - - public void setTextColor(ColorStateList colors) {} - - public final ColorStateList getTextColors() { - return null; - } - - public final int getCurrentTextColor() { - return 0; - } - - public void setHighlightColor(int color) {} - - public int getHighlightColor() { - return 0; - } - - public final void setShowSoftInputOnFocus(boolean show) {} - - public final boolean getShowSoftInputOnFocus() { - return false; - } - - public void setShadowLayer(float radius, float dx, float dy, int color) {} - - public float getShadowRadius() { - return 0; - } - - public float getShadowDx() { - return 0; - } - - public float getShadowDy() { - return 0; - } - - public int getShadowColor() { - return 0; - } - - public final void setAutoLinkMask(int mask) {} - - public final void setLinksClickable(boolean whether) {} - - public final boolean getLinksClickable() { - return false; - } - - public final void setHintTextColor(int color) {} - - public final void setHintTextColor(ColorStateList colors) {} - - public final ColorStateList getHintTextColors() { - return null; - } - - public final int getCurrentHintTextColor() { - return 0; - } - - public final void setLinkTextColor(int color) {} - - public final void setLinkTextColor(ColorStateList colors) {} - - public final ColorStateList getLinkTextColors() { - return null; - } - - public void setGravity(int gravity) {} - - public int getGravity() { - return 0; - } - - public int getPaintFlags() { - return 0; - } - - public void setPaintFlags(int flags) {} - - public void setHorizontallyScrolling(boolean whether) {} - - public final boolean isHorizontallyScrollable() { - return false; - } - - public boolean getHorizontallyScrolling() { - return false; - } - - public void setMinLines(int minLines) {} - - public int getMinLines() { - return 0; - } - - public void setMinHeight(int minPixels) {} - - public int getMinHeight() { - return 0; - } - - public void setMaxLines(int maxLines) {} - - public int getMaxLines() { - return 0; - } - - public void setMaxHeight(int maxPixels) {} - - public int getMaxHeight() { - return 0; - } - - public void setLines(int lines) {} - - public void setHeight(int pixels) {} - - public void setMinEms(int minEms) {} - - public int getMinEms() { - return 0; - } - - public void setMinWidth(int minPixels) {} - - public int getMinWidth() { - return 0; - } - - public void setMaxEms(int maxEms) {} - - public int getMaxEms() { - return 0; - } - - public void setMaxWidth(int maxPixels) {} - - public int getMaxWidth() { - return 0; - } - - public void setEms(int ems) {} - - public void setWidth(int pixels) {} - - public void setLineSpacing(float add, float mult) {} - - public float getLineSpacingMultiplier() { - return 0; - } - - public float getLineSpacingExtra() { - return 0; - } - - public void setLineHeight(int lineHeight) {} - - public final void append(CharSequence text) {} - - public void append(CharSequence text, int start, int end) {} - - - public void setFreezesText(boolean freezesText) {} - - public boolean getFreezesText() { - return false; - } - - public final void setText(CharSequence text) {} - - public final void setTextKeepState(CharSequence text) {} - - public void setText(CharSequence text, BufferType type) {} - - public final void setText(char[] text, int start, int len) {} - - public final void setTextKeepState(CharSequence text, BufferType type) {} - - public final void setText(int resid) {} - - public final void setText(int resid, BufferType type) {} - - public final void setHint(CharSequence hint) {} - - public final void setHint(int resid) {} - - public CharSequence getHint() { - return null; - } - - public boolean isSingleLine() { - return false; - } - - public void setInputType(int type) {} - - public boolean isAnyPasswordInputType() { - return false; - } - - public void setRawInputType(int type) {} - - public int getInputType() { - return 0; - } - - public void setImeOptions(int imeOptions) {} - - public int getImeOptions() { - return 0; - } - - public void setImeActionLabel(CharSequence label, int actionId) {} - - public CharSequence getImeActionLabel() { - return null; - } - - public int getImeActionId() { - return 0; - } - - public void onEditorAction(int actionCode) {} - - public void setPrivateImeOptions(String type) {} - - public String getPrivateImeOptions() { - return null; - } - - public Bundle getInputExtras(boolean create) { - return null; - } - - public void setImeHintLocales(LocaleList hintLocales) {} - - public LocaleList getImeHintLocales() { - return null; - } - - public CharSequence getError() { - return null; - } - - public void setError(CharSequence error) {} - - public void setError(CharSequence error, Drawable icon) {} - - public boolean isTextSelectable() { - return false; - } - - public void setTextIsSelectable(boolean selectable) {} - - public int getHorizontalOffsetForDrawables() { - return 0; - } - - public int getLineCount() { - return 0; - } - - public int getLineBounds(int line, Rect bounds) { - return 0; - } - - public int getBaseline() { - return 0; - } - - public void resetErrorChangedFlag() {} - - public void hideErrorIfUnchanged() {} - - public boolean onCheckIsTextEditor() { - return false; - } - - public void beginBatchEdit() {} - - public void endBatchEdit() {} - - public void onBeginBatchEdit() {} - - public void onEndBatchEdit() {} - - public boolean onPrivateIMECommand(String action, Bundle data) { - return false; - } - - public void nullLayouts() {} - - public boolean useDynamicLayout() { - return false; - } - - public void setIncludeFontPadding(boolean includepad) {} - - public boolean getIncludeFontPadding() { - return false; - } - - public boolean bringPointIntoView(int offset) { - return false; - } - - public boolean moveCursorToVisibleOffset() { - return false; - } - - public void computeScroll() {} - - public void debug(int depth) {} - - public int getSelectionStart() { - return 0; - } - - public int getSelectionEnd() { - return 0; - } - - public boolean hasSelection() { - return false; - } - - public void setSingleLine() {} - - public void setAllCaps(boolean allCaps) {} - - public boolean isAllCaps() { - return false; - } - - public void setSingleLine(boolean singleLine) {} - - public void setMarqueeRepeatLimit(int marqueeLimit) {} - - public int getMarqueeRepeatLimit() { - return 0; - } - - public void setSelectAllOnFocus(boolean selectAllOnFocus) {} - - public void setCursorVisible(boolean visible) {} - - public boolean isCursorVisible() { - return false; - } - - public void onWindowFocusChanged(boolean hasWindowFocus) {} - - public void clearComposingText() {} - - public void setSelected(boolean selected) {} - - public boolean showContextMenu() { - return false; - } - - public boolean showContextMenu(float x, float y) { - return false; - } - - public boolean didTouchFocusSelect() { - return false; - } - - public void cancelLongPress() {} - - public void findViewsWithText(ArrayList outViews, CharSequence searched, int flags) {} - - public enum BufferType { - } - - public static ColorStateList getTextColors(Context context, TypedArray attrs) { - return null; - } - - public static int getTextColor(Context context, TypedArray attrs, int def) { - return 0; - } - - public final void setTextOperationUser(UserHandle user) {} - - public Locale getTextServicesLocale() { - return null; - } - - public boolean isInExtractedMode() { - return false; - } - - public Locale getSpellCheckerLocale() { - return null; - } - - public CharSequence getAccessibilityClassName() { - return null; - } - - public boolean isPositionVisible(final float positionX, final float positionY) { - return false; - } - - public boolean performAccessibilityActionInternal(int action, Bundle arguments) { - return false; - } - - public void sendAccessibilityEventInternal(int eventType) {} - - public boolean isInputMethodTarget() { - return false; - } - - public boolean onTextContextMenuItem(int id) { - return false; - } - - public boolean performLongClick() { - return false; - } - - public boolean isSuggestionsEnabled() { - return false; - } - - public void hideFloatingToolbar(int durationMs) {} - - public int getOffsetForPosition(float x, float y) { - return 0; - } - - public void onRtlPropertiesChanged(int layoutDirection) {} - - public void onResolveDrawables(int layoutDirection) {} - - public CharSequence getIterableTextForAccessibility() { - return null; - } - - public int getAccessibilitySelectionStart() { - return 0; - } - - public boolean isAccessibilitySelectionExtendable() { - return false; - } - - public int getAccessibilitySelectionEnd() { - return 0; - } - - public void setAccessibilitySelection(int start, int end) {} - +import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.autofill.AutofillValue; +import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.ExtractedText; +import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnection; +import android.view.textclassifier.TextClassifier; +import android.view.translation.ViewTranslationRequest; +import android.widget.Scroller; +import java.util.ArrayList; +import java.util.Locale; +import java.util.function.Consumer; + +public class TextView extends View implements ViewTreeObserver.OnPreDrawListener +{ + protected TextView() {} + protected MovementMethod getDefaultMovementMethod(){ return null; } + protected boolean getDefaultEditable(){ return false; } + protected boolean isPaddingOffsetRequired(){ return false; } + protected boolean setFrame(int p0, int p1, int p2, int p3){ return false; } + protected boolean verifyDrawable(Drawable p0){ return false; } + protected float getLeftFadingEdgeStrength(){ return 0; } + protected float getRightFadingEdgeStrength(){ return 0; } + protected int computeHorizontalScrollRange(){ return 0; } + protected int computeVerticalScrollExtent(){ return 0; } + protected int computeVerticalScrollRange(){ return 0; } + protected int getBottomPaddingOffset(){ return 0; } + protected int getLeftPaddingOffset(){ return 0; } + protected int getRightPaddingOffset(){ return 0; } + protected int getTopPaddingOffset(){ return 0; } + protected int[] onCreateDrawableState(int p0){ return null; } + protected void drawableStateChanged(){} + protected void onAttachedToWindow(){} + protected void onConfigurationChanged(Configuration p0){} + protected void onCreateContextMenu(ContextMenu p0){} + protected void onDraw(Canvas p0){} + protected void onFocusChanged(boolean p0, int p1, Rect p2){} + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + protected void onMeasure(int p0, int p1){} + protected void onScrollChanged(int p0, int p1, int p2, int p3){} + protected void onSelectionChanged(int p0, int p1){} + protected void onTextChanged(CharSequence p0, int p1, int p2, int p3){} + protected void onVisibilityChanged(View p0, int p1){} + public ActionMode.Callback getCustomInsertionActionModeCallback(){ return null; } + public ActionMode.Callback getCustomSelectionActionModeCallback(){ return null; } + public AutofillValue getAutofillValue(){ return null; } + public BlendMode getCompoundDrawableTintBlendMode(){ return null; } + public Bundle getInputExtras(boolean p0){ return null; } + public CharSequence getAccessibilityClassName(){ return null; } + public CharSequence getError(){ return null; } + public CharSequence getHint(){ return null; } + public CharSequence getImeActionLabel(){ return null; } + public CharSequence getText(){ return null; } + public ColorStateList getCompoundDrawableTintList(){ return null; } + public ContentInfo onReceiveContent(ContentInfo p0){ return null; } + public Drawable getTextCursorDrawable(){ return null; } + public Drawable getTextSelectHandle(){ return null; } + public Drawable getTextSelectHandleLeft(){ return null; } + public Drawable getTextSelectHandleRight(){ return null; } + public Drawable[] getCompoundDrawables(){ return null; } + public Drawable[] getCompoundDrawablesRelative(){ return null; } + public Editable getEditableText(){ return null; } + public InputConnection onCreateInputConnection(EditorInfo p0){ return null; } + public InputFilter[] getFilters(){ return null; } + public Locale getTextLocale(){ return null; } + public LocaleList getImeHintLocales(){ return null; } + public LocaleList getTextLocales(){ return null; } + public Parcelable onSaveInstanceState(){ return null; } + public PointerIcon onResolvePointerIcon(MotionEvent p0, int p1){ return null; } + public PorterDuff.Mode getCompoundDrawableTintMode(){ return null; } + public PrecomputedText.Params getTextMetricsParams(){ return null; } + public String getFontFeatureSettings(){ return null; } + public String getFontVariationSettings(){ return null; } + public String getPrivateImeOptions(){ return null; } + public TextClassifier getTextClassifier(){ return null; } + public TextDirectionHeuristic getTextDirectionHeuristic(){ return null; } + public TextPaint getPaint(){ return null; } + public TextUtils.TruncateAt getEllipsize(){ return null; } + public TextView(Context p0){} + public TextView(Context p0, AttributeSet p1){} + public TextView(Context p0, AttributeSet p1, int p2){} + public TextView(Context p0, AttributeSet p1, int p2, int p3){} + public Typeface getTypeface(){ return null; } + public URLSpan[] getUrls(){ return null; } + public boolean bringPointIntoView(int p0){ return false; } + public boolean didTouchFocusSelect(){ return false; } + public boolean extractText(ExtractedTextRequest p0, ExtractedText p1){ return false; } + public boolean getFreezesText(){ return false; } + public boolean getIncludeFontPadding(){ return false; } + public boolean hasOverlappingRendering(){ return false; } + public boolean hasSelection(){ return false; } + public boolean isAllCaps(){ return false; } + public boolean isCursorVisible(){ return false; } + public boolean isElegantTextHeight(){ return false; } + public boolean isFallbackLineSpacing(){ return false; } + public boolean isInputMethodTarget(){ return false; } + public boolean isSingleLine(){ return false; } + public boolean isSuggestionsEnabled(){ return false; } + public boolean isTextSelectable(){ return false; } + public boolean moveCursorToVisibleOffset(){ return false; } + public boolean onCheckIsTextEditor(){ return false; } + public boolean onDragEvent(DragEvent p0){ return false; } + public boolean onGenericMotionEvent(MotionEvent p0){ return false; } + public boolean onKeyDown(int p0, KeyEvent p1){ return false; } + public boolean onKeyMultiple(int p0, int p1, KeyEvent p2){ return false; } + public boolean onKeyPreIme(int p0, KeyEvent p1){ return false; } + public boolean onKeyShortcut(int p0, KeyEvent p1){ return false; } + public boolean onKeyUp(int p0, KeyEvent p1){ return false; } + public boolean onPreDraw(){ return false; } + public boolean onPrivateIMECommand(String p0, Bundle p1){ return false; } + public boolean onTextContextMenuItem(int p0){ return false; } + public boolean onTouchEvent(MotionEvent p0){ return false; } + public boolean onTrackballEvent(MotionEvent p0){ return false; } + public boolean performLongClick(){ return false; } + public boolean setFontVariationSettings(String p0){ return false; } + public boolean showContextMenu(){ return false; } + public boolean showContextMenu(float p0, float p1){ return false; } + public final ColorStateList getHintTextColors(){ return null; } + public final ColorStateList getLinkTextColors(){ return null; } + public final ColorStateList getTextColors(){ return null; } + public final KeyListener getKeyListener(){ return null; } + public final Layout getLayout(){ return null; } + public final MovementMethod getMovementMethod(){ return null; } + public final TransformationMethod getTransformationMethod(){ return null; } + public final boolean getLinksClickable(){ return false; } + public final boolean getShowSoftInputOnFocus(){ return false; } + public final boolean isHorizontallyScrollable(){ return false; } + public final int getAutoLinkMask(){ return 0; } + public final int getCurrentHintTextColor(){ return 0; } + public final int getCurrentTextColor(){ return 0; } + public final void append(CharSequence p0){} + public final void setAutoLinkMask(int p0){} + public final void setEditableFactory(Editable.Factory p0){} + public final void setHint(CharSequence p0){} + public final void setHint(int p0){} + public final void setHintTextColor(ColorStateList p0){} + public final void setHintTextColor(int p0){} + public final void setLinkTextColor(ColorStateList p0){} + public final void setLinkTextColor(int p0){} + public final void setLinksClickable(boolean p0){} + public final void setMovementMethod(MovementMethod p0){} + public final void setShowSoftInputOnFocus(boolean p0){} + public final void setSpannableFactory(Spannable.Factory p0){} + public final void setText(CharSequence p0){} + public final void setText(char[] p0, int p1, int p2){} + public final void setText(int p0){} + public final void setText(int p0, TextView.BufferType p1){} + public final void setTextKeepState(CharSequence p0){} + public final void setTextKeepState(CharSequence p0, TextView.BufferType p1){} + public final void setTransformationMethod(TransformationMethod p0){} + public float getLetterSpacing(){ return 0; } + public float getLineSpacingExtra(){ return 0; } + public float getLineSpacingMultiplier(){ return 0; } + public float getShadowDx(){ return 0; } + public float getShadowDy(){ return 0; } + public float getShadowRadius(){ return 0; } + public float getTextScaleX(){ return 0; } + public float getTextSize(){ return 0; } + public int getAutoSizeMaxTextSize(){ return 0; } + public int getAutoSizeMinTextSize(){ return 0; } + public int getAutoSizeStepGranularity(){ return 0; } + public int getAutoSizeTextType(){ return 0; } + public int getAutofillType(){ return 0; } + public int getBaseline(){ return 0; } + public int getBreakStrategy(){ return 0; } + public int getCompoundDrawablePadding(){ return 0; } + public int getCompoundPaddingBottom(){ return 0; } + public int getCompoundPaddingEnd(){ return 0; } + public int getCompoundPaddingLeft(){ return 0; } + public int getCompoundPaddingRight(){ return 0; } + public int getCompoundPaddingStart(){ return 0; } + public int getCompoundPaddingTop(){ return 0; } + public int getExtendedPaddingBottom(){ return 0; } + public int getExtendedPaddingTop(){ return 0; } + public int getFirstBaselineToTopHeight(){ return 0; } + public int getGravity(){ return 0; } + public int getHighlightColor(){ return 0; } + public int getHyphenationFrequency(){ return 0; } + public int getImeActionId(){ return 0; } + public int getImeOptions(){ return 0; } + public int getInputType(){ return 0; } + public int getJustificationMode(){ return 0; } + public int getLastBaselineToBottomHeight(){ return 0; } + public int getLineBounds(int p0, Rect p1){ return 0; } + public int getLineCount(){ return 0; } + public int getLineHeight(){ return 0; } + public int getMarqueeRepeatLimit(){ return 0; } + public int getMaxEms(){ return 0; } + public int getMaxHeight(){ return 0; } + public int getMaxLines(){ return 0; } + public int getMaxWidth(){ return 0; } + public int getMinEms(){ return 0; } + public int getMinHeight(){ return 0; } + public int getMinLines(){ return 0; } + public int getMinWidth(){ return 0; } + public int getOffsetForPosition(float p0, float p1){ return 0; } + public int getPaintFlags(){ return 0; } + public int getSelectionEnd(){ return 0; } + public int getSelectionStart(){ return 0; } + public int getShadowColor(){ return 0; } + public int getTextSizeUnit(){ return 0; } + public int getTotalPaddingBottom(){ return 0; } + public int getTotalPaddingEnd(){ return 0; } + public int getTotalPaddingLeft(){ return 0; } + public int getTotalPaddingRight(){ return 0; } + public int getTotalPaddingStart(){ return 0; } + public int getTotalPaddingTop(){ return 0; } + public int length(){ return 0; } + public int[] getAutoSizeTextAvailableSizes(){ return null; } + public static int AUTO_SIZE_TEXT_TYPE_NONE = 0; + public static int AUTO_SIZE_TEXT_TYPE_UNIFORM = 0; + public void addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo p0, String p1, Bundle p2){} + public void addTextChangedListener(TextWatcher p0){} + public void append(CharSequence p0, int p1, int p2){} + public void autofill(AutofillValue p0){} + public void beginBatchEdit(){} + public void cancelLongPress(){} + public void clearComposingText(){} + public void computeScroll(){} + public void debug(int p0){} + public void drawableHotspotChanged(float p0, float p1){} + public void endBatchEdit(){} + public void findViewsWithText(ArrayList p0, CharSequence p1, int p2){} + public void getFocusedRect(Rect p0){} + public void invalidateDrawable(Drawable p0){} + public void jumpDrawablesToCurrentState(){} + public void onBeginBatchEdit(){} + public void onCommitCompletion(CompletionInfo p0){} + public void onCommitCorrection(CorrectionInfo p0){} + public void onCreateViewTranslationRequest(int[] p0, Consumer p1){} + public void onEditorAction(int p0){} + public void onEndBatchEdit(){} + public void onRestoreInstanceState(Parcelable p0){} + public void onRtlPropertiesChanged(int p0){} + public void onScreenStateChanged(int p0){} + public void onWindowFocusChanged(boolean p0){} + public void removeTextChangedListener(TextWatcher p0){} + public void sendAccessibilityEventUnchecked(AccessibilityEvent p0){} + public void setAllCaps(boolean p0){} + public void setAutoSizeTextTypeUniformWithConfiguration(int p0, int p1, int p2, int p3){} + public void setAutoSizeTextTypeUniformWithPresetSizes(int[] p0, int p1){} + public void setAutoSizeTextTypeWithDefaults(int p0){} + public void setBreakStrategy(int p0){} + public void setCompoundDrawablePadding(int p0){} + public void setCompoundDrawableTintBlendMode(BlendMode p0){} + public void setCompoundDrawableTintList(ColorStateList p0){} + public void setCompoundDrawableTintMode(PorterDuff.Mode p0){} + public void setCompoundDrawables(Drawable p0, Drawable p1, Drawable p2, Drawable p3){} + public void setCompoundDrawablesRelative(Drawable p0, Drawable p1, Drawable p2, Drawable p3){} + public void setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable p0, Drawable p1, Drawable p2, Drawable p3){} + public void setCompoundDrawablesRelativeWithIntrinsicBounds(int p0, int p1, int p2, int p3){} + public void setCompoundDrawablesWithIntrinsicBounds(Drawable p0, Drawable p1, Drawable p2, Drawable p3){} + public void setCompoundDrawablesWithIntrinsicBounds(int p0, int p1, int p2, int p3){} + public void setCursorVisible(boolean p0){} + public void setCustomInsertionActionModeCallback(ActionMode.Callback p0){} + public void setCustomSelectionActionModeCallback(ActionMode.Callback p0){} + public void setElegantTextHeight(boolean p0){} + public void setEllipsize(TextUtils.TruncateAt p0){} + public void setEms(int p0){} + public void setEnabled(boolean p0){} + public void setError(CharSequence p0){} + public void setError(CharSequence p0, Drawable p1){} + public void setExtractedText(ExtractedText p0){} + public void setFallbackLineSpacing(boolean p0){} + public void setFilters(InputFilter[] p0){} + public void setFirstBaselineToTopHeight(int p0){} + public void setFontFeatureSettings(String p0){} + public void setFreezesText(boolean p0){} + public void setGravity(int p0){} + public void setHeight(int p0){} + public void setHighlightColor(int p0){} + public void setHorizontallyScrolling(boolean p0){} + public void setHyphenationFrequency(int p0){} + public void setImeActionLabel(CharSequence p0, int p1){} + public void setImeHintLocales(LocaleList p0){} + public void setImeOptions(int p0){} + public void setIncludeFontPadding(boolean p0){} + public void setInputExtras(int p0){} + public void setInputType(int p0){} + public void setJustificationMode(int p0){} + public void setKeyListener(KeyListener p0){} + public void setLastBaselineToBottomHeight(int p0){} + public void setLetterSpacing(float p0){} + public void setLineHeight(int p0){} + public void setLineSpacing(float p0, float p1){} + public void setLines(int p0){} + public void setMarqueeRepeatLimit(int p0){} + public void setMaxEms(int p0){} + public void setMaxHeight(int p0){} + public void setMaxLines(int p0){} + public void setMaxWidth(int p0){} + public void setMinEms(int p0){} + public void setMinHeight(int p0){} + public void setMinLines(int p0){} + public void setMinWidth(int p0){} + public void setOnEditorActionListener(TextView.OnEditorActionListener p0){} + public void setPadding(int p0, int p1, int p2, int p3){} + public void setPaddingRelative(int p0, int p1, int p2, int p3){} + public void setPaintFlags(int p0){} + public void setPrivateImeOptions(String p0){} + public void setRawInputType(int p0){} + public void setScroller(Scroller p0){} + public void setSelectAllOnFocus(boolean p0){} + public void setSelected(boolean p0){} + public void setShadowLayer(float p0, float p1, float p2, int p3){} + public void setSingleLine(){} + public void setSingleLine(boolean p0){} + public void setText(CharSequence p0, TextView.BufferType p1){} + public void setTextAppearance(Context p0, int p1){} + public void setTextAppearance(int p0){} + public void setTextClassifier(TextClassifier p0){} + public void setTextColor(ColorStateList p0){} + public void setTextColor(int p0){} + public void setTextCursorDrawable(Drawable p0){} + public void setTextCursorDrawable(int p0){} + public void setTextIsSelectable(boolean p0){} + public void setTextLocale(Locale p0){} + public void setTextLocales(LocaleList p0){} + public void setTextMetricsParams(PrecomputedText.Params p0){} + public void setTextScaleX(float p0){} + public void setTextSelectHandle(Drawable p0){} + public void setTextSelectHandle(int p0){} + public void setTextSelectHandleLeft(Drawable p0){} + public void setTextSelectHandleLeft(int p0){} + public void setTextSelectHandleRight(Drawable p0){} + public void setTextSelectHandleRight(int p0){} + public void setTextSize(float p0){} + public void setTextSize(int p0, float p1){} + public void setTypeface(Typeface p0){} + public void setTypeface(Typeface p0, int p1){} + public void setWidth(int p0){} + static public enum BufferType + { + EDITABLE, NORMAL, SPANNABLE; + private BufferType() {} + } + static public interface OnEditorActionListener + { + boolean onEditorAction(TextView p0, int p1, KeyEvent p2); + } } diff --git a/java/ql/test/stubs/google-android-9.0.0/android/widget/Toolbar.java b/java/ql/test/stubs/google-android-9.0.0/android/widget/Toolbar.java new file mode 100644 index 00000000000..91b6be9e53d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/widget/Toolbar.java @@ -0,0 +1,116 @@ +// Generated automatically from android.widget.Toolbar for testing purposes + +package android.widget; + +import android.app.ActionBar; +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Parcelable; +import android.util.AttributeSet; +import android.view.Menu; +import android.view.MenuItem; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; + +public class Toolbar extends ViewGroup +{ + protected Toolbar() {} + protected Parcelable onSaveInstanceState(){ return null; } + protected Toolbar.LayoutParams generateDefaultLayoutParams(){ return null; } + protected Toolbar.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p0){ return null; } + protected boolean checkLayoutParams(ViewGroup.LayoutParams p0){ return false; } + protected void onAttachedToWindow(){} + protected void onDetachedFromWindow(){} + protected void onLayout(boolean p0, int p1, int p2, int p3, int p4){} + protected void onMeasure(int p0, int p1){} + protected void onRestoreInstanceState(Parcelable p0){} + public CharSequence getCollapseContentDescription(){ return null; } + public CharSequence getLogoDescription(){ return null; } + public CharSequence getNavigationContentDescription(){ return null; } + public CharSequence getSubtitle(){ return null; } + public CharSequence getTitle(){ return null; } + public Drawable getCollapseIcon(){ return null; } + public Drawable getLogo(){ return null; } + public Drawable getNavigationIcon(){ return null; } + public Drawable getOverflowIcon(){ return null; } + public Menu getMenu(){ return null; } + public Toolbar(Context p0){} + public Toolbar(Context p0, AttributeSet p1){} + public Toolbar(Context p0, AttributeSet p1, int p2){} + public Toolbar(Context p0, AttributeSet p1, int p2, int p3){} + public Toolbar.LayoutParams generateLayoutParams(AttributeSet p0){ return null; } + public boolean hasExpandedActionView(){ return false; } + public boolean hideOverflowMenu(){ return false; } + public boolean isOverflowMenuShowing(){ return false; } + public boolean onTouchEvent(MotionEvent p0){ return false; } + public boolean showOverflowMenu(){ return false; } + public int getContentInsetEnd(){ return 0; } + public int getContentInsetEndWithActions(){ return 0; } + public int getContentInsetLeft(){ return 0; } + public int getContentInsetRight(){ return 0; } + public int getContentInsetStart(){ return 0; } + public int getContentInsetStartWithNavigation(){ return 0; } + public int getCurrentContentInsetEnd(){ return 0; } + public int getCurrentContentInsetLeft(){ return 0; } + public int getCurrentContentInsetRight(){ return 0; } + public int getCurrentContentInsetStart(){ return 0; } + public int getPopupTheme(){ return 0; } + public int getTitleMarginBottom(){ return 0; } + public int getTitleMarginEnd(){ return 0; } + public int getTitleMarginStart(){ return 0; } + public int getTitleMarginTop(){ return 0; } + public void collapseActionView(){} + public void dismissPopupMenus(){} + public void inflateMenu(int p0){} + public void onRtlPropertiesChanged(int p0){} + public void setCollapseContentDescription(CharSequence p0){} + public void setCollapseContentDescription(int p0){} + public void setCollapseIcon(Drawable p0){} + public void setCollapseIcon(int p0){} + public void setContentInsetEndWithActions(int p0){} + public void setContentInsetStartWithNavigation(int p0){} + public void setContentInsetsAbsolute(int p0, int p1){} + public void setContentInsetsRelative(int p0, int p1){} + public void setLogo(Drawable p0){} + public void setLogo(int p0){} + public void setLogoDescription(CharSequence p0){} + public void setLogoDescription(int p0){} + public void setNavigationContentDescription(CharSequence p0){} + public void setNavigationContentDescription(int p0){} + public void setNavigationIcon(Drawable p0){} + public void setNavigationIcon(int p0){} + public void setNavigationOnClickListener(View.OnClickListener p0){} + public void setOnMenuItemClickListener(Toolbar.OnMenuItemClickListener p0){} + public void setOverflowIcon(Drawable p0){} + public void setPopupTheme(int p0){} + public void setSubtitle(CharSequence p0){} + public void setSubtitle(int p0){} + public void setSubtitleTextAppearance(Context p0, int p1){} + public void setSubtitleTextColor(int p0){} + public void setTitle(CharSequence p0){} + public void setTitle(int p0){} + public void setTitleMargin(int p0, int p1, int p2, int p3){} + public void setTitleMarginBottom(int p0){} + public void setTitleMarginEnd(int p0){} + public void setTitleMarginStart(int p0){} + public void setTitleMarginTop(int p0){} + public void setTitleTextAppearance(Context p0, int p1){} + public void setTitleTextColor(int p0){} + static public class LayoutParams extends ActionBar.LayoutParams + { + protected LayoutParams() {} + public LayoutParams(ActionBar.LayoutParams p0){} + public LayoutParams(Context p0, AttributeSet p1){} + public LayoutParams(Toolbar.LayoutParams p0){} + public LayoutParams(ViewGroup.LayoutParams p0){} + public LayoutParams(ViewGroup.MarginLayoutParams p0){} + public LayoutParams(int p0){} + public LayoutParams(int p0, int p1){} + public LayoutParams(int p0, int p1, int p2){} + } + static public interface OnMenuItemClickListener + { + boolean onMenuItemClick(MenuItem p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreen.java b/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreen.java new file mode 100644 index 00000000000..1c927807b45 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreen.java @@ -0,0 +1,16 @@ +// Generated automatically from android.window.SplashScreen for testing purposes + +package android.window; + +import android.window.SplashScreenView; + +public interface SplashScreen +{ + static public interface OnExitAnimationListener + { + void onSplashScreenExit(SplashScreenView p0); + } + void clearOnExitAnimationListener(); + void setOnExitAnimationListener(SplashScreen.OnExitAnimationListener p0); + void setSplashScreenTheme(int p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreenView.java b/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreenView.java new file mode 100644 index 00000000000..01ad6a2cd0b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/window/SplashScreenView.java @@ -0,0 +1,18 @@ +// Generated automatically from android.window.SplashScreenView for testing purposes + +package android.window; + +import android.view.View; +import android.widget.FrameLayout; +import java.time.Duration; +import java.time.Instant; + +public class SplashScreenView extends FrameLayout +{ + protected void onDetachedFromWindow(){} + public Duration getIconAnimationDuration(){ return null; } + public Instant getIconAnimationStart(){ return null; } + public View getIconView(){ return null; } + public void remove(){} + public void setAlpha(float p0){} +} diff --git a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java index 8eadda7e63a..101b8094fdd 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java +++ b/javascript/extractor/src/com/semmle/js/extractor/AutoBuild.java @@ -86,8 +86,6 @@ import com.semmle.util.trap.TrapWriter; * XML is also supported *

  • LGTM_INDEX_XML_MODE: whether to extract XML files *
  • LGTM_THREADS: the maximum number of files to extract in parallel - *
  • LGTM_TRAP_CACHE: the path of a directory to use for trap caching - *
  • LGTM_TRAP_CACHE_BOUND: the size to bound the trap cache to * * *

    It extracts the following: @@ -220,7 +218,7 @@ public class AutoBuild { this.LGTM_SRC = toRealPath(getPathFromEnvVar("LGTM_SRC")); this.SEMMLE_DIST = Paths.get(EnvironmentVariables.getExtractorRoot()); this.outputConfig = new ExtractorOutputConfig(LegacyLanguage.JAVASCRIPT); - this.trapCache = mkTrapCache(); + this.trapCache = ITrapCache.fromExtractorOptions(); this.typeScriptMode = getEnumFromEnvVar("LGTM_INDEX_TYPESCRIPT", TypeScriptMode.class, TypeScriptMode.FULL); this.defaultEncoding = getEnvVar("LGTM_INDEX_DEFAULT_ENCODING"); @@ -281,28 +279,6 @@ public class AutoBuild { } } - /** - * Set up TRAP cache based on environment variables LGTM_TRAP_CACHE and - * LGTM_TRAP_CACHE_BOUND. - */ - private ITrapCache mkTrapCache() { - ITrapCache trapCache; - String trapCachePath = getEnvVar("LGTM_TRAP_CACHE"); - if (trapCachePath != null) { - Long sizeBound = null; - String trapCacheBound = getEnvVar("LGTM_TRAP_CACHE_BOUND"); - if (trapCacheBound != null) { - sizeBound = DefaultTrapCache.asFileSize(trapCacheBound); - if (sizeBound == null) - throw new UserError("Invalid TRAP cache size bound: " + trapCacheBound); - } - trapCache = new DefaultTrapCache(trapCachePath, sizeBound, Main.EXTRACTOR_VERSION); - } else { - trapCache = new DummyTrapCache(); - } - return trapCache; - } - private void setupFileTypes() { for (String spec : Main.NEWLINE.split(getEnvVar("LGTM_INDEX_FILETYPES", ""))) { spec = spec.trim(); @@ -513,14 +489,13 @@ public class AutoBuild { SEMMLE_DIST.resolve(".cache").resolve("trap-cache").resolve("javascript"); if (Files.isDirectory(trapCachePath)) { trapCache = - new DefaultTrapCache(trapCachePath.toString(), null, Main.EXTRACTOR_VERSION) { + new DefaultTrapCache(trapCachePath.toString(), null, Main.EXTRACTOR_VERSION, false) { boolean warnedAboutCacheMiss = false; @Override public File lookup(String source, ExtractorConfig config, FileType type) { File f = super.lookup(source, config, type); - // only return `f` if it exists; this has the effect of making the cache read-only - if (f.exists()) return f; + if (f != null) return f; // warn on first failed lookup if (!warnedAboutCacheMiss) { warn("Trap cache lookup for externs failed."); diff --git a/javascript/extractor/src/com/semmle/js/extractor/ExtractorOptionsUtil.java b/javascript/extractor/src/com/semmle/js/extractor/ExtractorOptionsUtil.java new file mode 100644 index 00000000000..c58f27b2c8a --- /dev/null +++ b/javascript/extractor/src/com/semmle/js/extractor/ExtractorOptionsUtil.java @@ -0,0 +1,12 @@ +package com.semmle.js.extractor; + +import com.semmle.util.process.Env; + +public class ExtractorOptionsUtil { + public static String readExtractorOption(String... option) { + StringBuilder name = new StringBuilder("CODEQL_EXTRACTOR_JAVASCRIPT_OPTION"); + for (String segment : option) + name.append("_").append(segment.toUpperCase()); + return Env.systemEnv().getNonEmpty(name.toString()); + } +} diff --git a/javascript/extractor/src/com/semmle/js/extractor/Main.java b/javascript/extractor/src/com/semmle/js/extractor/Main.java index c001d22dfa9..9929195bb77 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/Main.java +++ b/javascript/extractor/src/com/semmle/js/extractor/Main.java @@ -15,8 +15,6 @@ import com.semmle.extractor.html.HtmlPopulator; import com.semmle.js.extractor.ExtractorConfig.Platform; import com.semmle.js.extractor.ExtractorConfig.SourceType; import com.semmle.js.extractor.FileExtractor.FileType; -import com.semmle.js.extractor.trapcache.DefaultTrapCache; -import com.semmle.js.extractor.trapcache.DummyTrapCache; import com.semmle.js.extractor.trapcache.ITrapCache; import com.semmle.js.parser.ParsedProject; import com.semmle.ts.extractor.TypeExtractor; @@ -61,8 +59,6 @@ public class Main { private static final String P_PLATFORM = "--platform"; private static final String P_QUIET = "--quiet"; private static final String P_SOURCE_TYPE = "--source-type"; - private static final String P_TRAP_CACHE = "--trap-cache"; - private static final String P_TRAP_CACHE_BOUND = "--trap-cache-bound"; private static final String P_TYPESCRIPT = "--typescript"; private static final String P_TYPESCRIPT_FULL = "--typescript-full"; private static final String P_TYPESCRIPT_RAM = "--typescript-ram"; @@ -112,22 +108,7 @@ public class Main { ap.parse(); extractorConfig = parseJSOptions(ap); - ITrapCache trapCache; - if (ap.has(P_TRAP_CACHE)) { - Long sizeBound = null; - if (ap.has(P_TRAP_CACHE_BOUND)) { - String tcb = ap.getString(P_TRAP_CACHE_BOUND); - sizeBound = DefaultTrapCache.asFileSize(tcb); - if (sizeBound == null) ap.error("Invalid TRAP cache size bound: " + tcb); - } - trapCache = new DefaultTrapCache(ap.getString(P_TRAP_CACHE), sizeBound, EXTRACTOR_VERSION); - } else { - if (ap.has(P_TRAP_CACHE_BOUND)) - ap.error( - P_TRAP_CACHE_BOUND + " should only be specified together with " + P_TRAP_CACHE + "."); - trapCache = new DummyTrapCache(); - } - fileExtractor = new FileExtractor(extractorConfig, extractorOutputConfig, trapCache); + fileExtractor = new FileExtractor(extractorConfig, extractorOutputConfig, ITrapCache.fromExtractorOptions()); setupMatchers(ap); @@ -432,12 +413,6 @@ public class Main { argsParser.addToleratedFlag(P_TOLERATE_PARSE_ERRORS, 0); argsParser.addFlag( P_ABORT_ON_PARSE_ERRORS, 0, "Abort extraction if a parse error is encountered."); - argsParser.addFlag(P_TRAP_CACHE, 1, "Use the given directory as the TRAP cache."); - argsParser.addFlag( - P_TRAP_CACHE_BOUND, - 1, - "A (soft) upper limit on the size of the TRAP cache, " - + "in standard size units (e.g., 'g' for gigabytes)."); argsParser.addFlag(P_DEFAULT_ENCODING, 1, "The encoding to use; default is UTF-8."); argsParser.addFlag(P_TYPESCRIPT, 0, "Enable basic TypesScript support."); argsParser.addFlag( diff --git a/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java b/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java index 6caa29e8f63..c96be38c7d2 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java +++ b/javascript/extractor/src/com/semmle/js/extractor/trapcache/DefaultTrapCache.java @@ -26,9 +26,15 @@ public class DefaultTrapCache implements ITrapCache { */ private final String extractorVersion; - public DefaultTrapCache(String trapCache, Long sizeBound, String extractorVersion) { + /** + * Whether this cache supports write operations. + */ + private final boolean writeable; + + public DefaultTrapCache(String trapCache, Long sizeBound, String extractorVersion, boolean writeable) { this.trapCache = new File(trapCache); this.extractorVersion = extractorVersion; + this.writeable = writeable; try { initCache(sizeBound); } catch (ResourceError | SecurityException e) { @@ -135,6 +141,8 @@ public class DefaultTrapCache implements ITrapCache { digestor.write(type.toString()); digestor.write(config); digestor.write(source); - return new File(trapCache, digestor.getDigest() + ".trap.gz"); + File result = new File(trapCache, digestor.getDigest() + ".trap.gz"); + if (!writeable && !result.exists()) return null; // If the cache isn't writable, only return the file if it exists + return result; } } diff --git a/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java b/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java index 745e930e75e..fb7b553e14c 100644 --- a/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java +++ b/javascript/extractor/src/com/semmle/js/extractor/trapcache/ITrapCache.java @@ -1,7 +1,11 @@ package com.semmle.js.extractor.trapcache; +import static com.semmle.js.extractor.ExtractorOptionsUtil.readExtractorOption; + import com.semmle.js.extractor.ExtractorConfig; import com.semmle.js.extractor.FileExtractor; +import com.semmle.js.extractor.Main; +import com.semmle.util.exception.UserError; import java.io.File; /** Generic TRAP cache interface. */ @@ -18,4 +22,29 @@ public interface ITrapCache { * cached information), or does not yet exist (and should be populated by the extractor) */ public File lookup(String source, ExtractorConfig config, FileExtractor.FileType type); + + /** + * Build a TRAP cache as defined by the extractor options, which are read from the corresponding + * environment variables as defined in + * https://github.com/github/codeql-core/blob/main/design/spec/codeql-extractors.md + * + * @return a TRAP cache + */ + public static ITrapCache fromExtractorOptions() { + String trapCachePath = readExtractorOption("trap", "cache", "dir"); + if (trapCachePath != null) { + Long sizeBound = null; + String trapCacheBound = readExtractorOption("trap", "cache", "bound"); + if (trapCacheBound != null) { + sizeBound = DefaultTrapCache.asFileSize(trapCacheBound); + if (sizeBound == null) + throw new UserError("Invalid TRAP cache size bound: " + trapCacheBound); + } + boolean writeable = true; + String trapCacheWrite = readExtractorOption("trap", "cache", "write"); + if (trapCacheWrite != null) writeable = trapCacheWrite.equalsIgnoreCase("TRUE"); + return new DefaultTrapCache(trapCachePath, sizeBound, Main.EXTRACTOR_VERSION, writeable); + } + return new DummyTrapCache(); + } } diff --git a/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql b/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql index 2286fdc2121..b146b19e54d 100644 --- a/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql +++ b/javascript/ql/examples/queries/dataflow/TemplateInjection/TemplateInjection.ql @@ -14,7 +14,7 @@ import DataFlow::PathGraph /** * Gets the name of an unescaped placeholder in a lodash template. * - * For example, the string `

    <%= title %>

    ` contains the placeholder `title`. + * For example, the string `"

    <%= title %>

    "` contains the placeholder "title". */ bindingset[s] string getAPlaceholderInString(string s) { diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll index 07ee16fda50..53d559d2568 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/extraction/ExtractEndpointData.qll @@ -188,10 +188,10 @@ module FlowFromSource { Query getQuery() { result = q } - /** The sinks are the endpoints we're extracting. */ + /** Holds if `sink` is an endpoint we're extracting. */ override predicate isSink(DataFlow::Node sink) { sink = getAnEndpoint(q) } - /** The sinks are the endpoints we're extracting. */ + /** Holds if `sink` is an endpoint we're extracting. */ override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel lbl) { sink = getAnEndpoint(q) and exists(lbl) } diff --git a/javascript/ql/lib/CHANGELOG.md b/javascript/ql/lib/CHANGELOG.md index 9df72979ef3..6f359e0ac85 100644 --- a/javascript/ql/lib/CHANGELOG.md +++ b/javascript/ql/lib/CHANGELOG.md @@ -1,3 +1,13 @@ +## 0.2.2 + +## 0.2.1 + +### Minor Analysis Improvements + +* The `chownr` library is now modeled as a sink for the `js/path-injection` query. +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). +* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query. + ## 0.2.0 ### Major Analysis Improvements diff --git a/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words copy.md b/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words copy.md deleted file mode 100644 index 8a0151df015..00000000000 --- a/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words copy.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query. diff --git a/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words.md b/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words.md deleted file mode 100644 index 9102d58abdb..00000000000 --- a/javascript/ql/lib/change-notes/2022-06-22-sensitive-common-words.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). diff --git a/javascript/ql/lib/change-notes/2022-06-30-chownr.md b/javascript/ql/lib/change-notes/2022-06-30-chownr.md deleted file mode 100644 index 1ad13fb8113..00000000000 --- a/javascript/ql/lib/change-notes/2022-06-30-chownr.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* The `chownr` library is now modeled as a sink for the `js/path-injection` query. diff --git a/javascript/ql/lib/change-notes/released/0.2.1.md b/javascript/ql/lib/change-notes/released/0.2.1.md new file mode 100644 index 00000000000..3548bd5ad69 --- /dev/null +++ b/javascript/ql/lib/change-notes/released/0.2.1.md @@ -0,0 +1,7 @@ +## 0.2.1 + +### Minor Analysis Improvements + +* The `chownr` library is now modeled as a sink for the `js/path-injection` query. +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). +* The `gray-matter` library is now modeled as a sink for the `js/code-injection` query. diff --git a/javascript/ql/lib/change-notes/released/0.2.2.md b/javascript/ql/lib/change-notes/released/0.2.2.md new file mode 100644 index 00000000000..fc31cbd3d6f --- /dev/null +++ b/javascript/ql/lib/change-notes/released/0.2.2.md @@ -0,0 +1 @@ +## 0.2.2 diff --git a/javascript/ql/lib/codeql-pack.release.yml b/javascript/ql/lib/codeql-pack.release.yml index 5274e27ed52..16a06790aa8 100644 --- a/javascript/ql/lib/codeql-pack.release.yml +++ b/javascript/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.2.2 diff --git a/javascript/ql/lib/definitions.qll b/javascript/ql/lib/definitions.qll index 4d0c0d50176..7b4806b1478 100644 --- a/javascript/ql/lib/definitions.qll +++ b/javascript/ql/lib/definitions.qll @@ -45,7 +45,7 @@ private predicate variableDefLookup(VarAccess va, AstNode def, string kind) { /** * Holds if variable access `va` is of kind `kind` and refers to the - * variable declaration. + * variable declaration `decl`. * * For example, in the statement `var x = 42, y = x;`, the initializing * expression of `y` is a variable access `x` of kind `"V"` that refers to diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index b16a8912ccf..e559e82a56a 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-all -version: 0.2.1-dev +version: 0.2.3-dev groups: javascript dbscheme: semmlecode.javascript.dbscheme extractor: javascript diff --git a/javascript/ql/lib/semmle/javascript/Actions.qll b/javascript/ql/lib/semmle/javascript/Actions.qll index f5b2c39b064..8ea789c96e0 100644 --- a/javascript/ql/lib/semmle/javascript/Actions.qll +++ b/javascript/ql/lib/semmle/javascript/Actions.qll @@ -28,6 +28,9 @@ module Actions { /** Gets the `jobs` mapping from job IDs to job definitions in this workflow. */ YAMLMapping getJobs() { result = this.lookup("jobs") } + /** Gets the name of the workflow. */ + string getName() { result = this.lookup("name").(YAMLString).getValue() } + /** Gets the name of the workflow file. */ string getFileName() { result = this.getFile().getBaseName() } @@ -129,6 +132,9 @@ module Actions { /** Gets the value of the `if` field in this step, if any. */ StepIf getIf() { result.getStep() = this } + + /** Gets the ID of this step, if any. */ + string getId() { result = this.lookup("id").(YAMLString).getValue() } } /** diff --git a/javascript/ql/lib/semmle/javascript/Arrays.qll b/javascript/ql/lib/semmle/javascript/Arrays.qll index c129e9a31b2..3598233f6ab 100644 --- a/javascript/ql/lib/semmle/javascript/Arrays.qll +++ b/javascript/ql/lib/semmle/javascript/Arrays.qll @@ -36,12 +36,18 @@ module ArrayTaintTracking { succ = call ) or - // `array.filter(x => x)` keeps the taint + // `array.filter(x => x)` and `array.filter(x => !!x)` keeps the taint call.(DataFlow::MethodCallNode).getMethodName() = "filter" and pred = call.getReceiver() and succ = call and - exists(DataFlow::FunctionNode callback | callback = call.getArgument(0).getAFunctionValue() | - callback.getParameter(0).getALocalUse() = callback.getAReturn() + exists(DataFlow::FunctionNode callback, DataFlow::Node param, DataFlow::Node ret | + callback = call.getArgument(0).getAFunctionValue() and + param = callback.getParameter(0).getALocalUse() and + ret = callback.getAReturn() + | + param = ret + or + param = DataFlow::exprNode(ret.asExpr().(LogNotExpr).getOperand().(LogNotExpr).getOperand()) ) or // `array.reduce` with tainted value in callback diff --git a/javascript/ql/lib/semmle/javascript/BasicBlocks.qll b/javascript/ql/lib/semmle/javascript/BasicBlocks.qll index 2b02b6b6486..6e6579d6f7e 100644 --- a/javascript/ql/lib/semmle/javascript/BasicBlocks.qll +++ b/javascript/ql/lib/semmle/javascript/BasicBlocks.qll @@ -146,7 +146,7 @@ class BasicBlock extends @cfg_node, NodeInStmtContainer { /** Holds if this basic block uses variable `v` in its `i`th node `u`. */ predicate useAt(int i, Variable v, VarUse u) { useAt(this, i, v, u) } - /** Holds if this basic block defines variable `v` in its `i`th node `u`. */ + /** Holds if this basic block defines variable `v` in its `i`th node `d`. */ predicate defAt(int i, Variable v, VarDef d) { defAt(this, i, v, d) } /** diff --git a/javascript/ql/lib/semmle/javascript/CharacterEscapes.qll b/javascript/ql/lib/semmle/javascript/CharacterEscapes.qll index 1a19112cee3..5c8dd2bdd06 100644 --- a/javascript/ql/lib/semmle/javascript/CharacterEscapes.qll +++ b/javascript/ql/lib/semmle/javascript/CharacterEscapes.qll @@ -75,7 +75,7 @@ module CharacterEscapes { } /** - * Gets a character in `n` that is preceded by a single useless backslash, resulting in a likely regular expression mistake explained by `mistake`. + * Gets a character in `src` that is preceded by a single useless backslash, resulting in a likely regular expression mistake explained by `mistake`. * * The character is the `i`th character of the raw string value of `rawStringNode`. */ diff --git a/javascript/ql/lib/semmle/javascript/Classes.qll b/javascript/ql/lib/semmle/javascript/Classes.qll index 5004ccc9b5f..23dc738345d 100644 --- a/javascript/ql/lib/semmle/javascript/Classes.qll +++ b/javascript/ql/lib/semmle/javascript/Classes.qll @@ -172,7 +172,7 @@ class ClassDefinition extends @class_definition, ClassOrInterface, AST::ValueNod /** Gets the expression denoting the super class of the defined class, if any. */ override Expr getSuperClass() { result = this.getChildExpr(1) } - /** Gets the `n`th type from the `implements` clause of this class, starting at 0. */ + /** Gets the `i`th type from the `implements` clause of this class, starting at 0. */ override TypeExpr getSuperInterface(int i) { // AST indices for super interfaces: -1, -4, -7, ... exists(int astIndex | typeexprs(result, _, this, astIndex, _) | diff --git a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll index 6e1be383362..f6e759b87e7 100644 --- a/javascript/ql/lib/semmle/javascript/ES2015Modules.qll +++ b/javascript/ql/lib/semmle/javascript/ES2015Modules.qll @@ -54,7 +54,7 @@ private predicate hasNamedExports(ES2015Module mod) { } /** - * Holds if this module contains a `default` export. + * Holds if this module contains a default export. */ private predicate hasDefaultExport(ES2015Module mod) { // export default foo; @@ -337,7 +337,7 @@ class BulkReExportDeclaration extends ReExportDeclaration, @export_all_declarati } /** - * Holds if the given bulk export should not re-export `name` because there is an explicit export + * Holds if the given bulk export `reExport` should not re-export `name` because there is an explicit export * of that name in the same module. * * At compile time, shadowing works across declaration spaces. diff --git a/javascript/ql/lib/semmle/javascript/JSON.qll b/javascript/ql/lib/semmle/javascript/JSON.qll index d474965051e..8230099818c 100644 --- a/javascript/ql/lib/semmle/javascript/JSON.qll +++ b/javascript/ql/lib/semmle/javascript/JSON.qll @@ -54,7 +54,9 @@ class JsonValue extends @json_value, Locatable { int getIntValue() { result = this.(JsonNumber).getValue().toInt() } /** If this is a boolean constant, gets its boolean value. */ - boolean getBooleanValue() { result.toString() = this.(JsonBoolean).getValue() } + boolean getBooleanValue() { + result.toString() = this.(JsonBoolean).getValue() and result = [true, false] + } override string getAPrimaryQlClass() { result = "JsonValue" } } diff --git a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll index cc032c3a20d..4c2fca26dc2 100644 --- a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll +++ b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll @@ -148,7 +148,11 @@ private string getASrcFolderName() { result = ["ts", "js", "src", "lib"] } class MainModulePath extends PathExpr, @json_string { PackageJson pkg; - MainModulePath() { this = pkg.getPropValue(["main", "module"]) } + MainModulePath() { + this = pkg.getPropValue(["main", "module"]) + or + this = getAJsonChild*(pkg.getPropValue("exports")) + } /** Gets the `package.json` file in which this path occurs. */ PackageJson getPackageJson() { result = pkg } @@ -164,6 +168,9 @@ class MainModulePath extends PathExpr, @json_string { } } +/** Gets the value of a property from the JSON object `obj`. */ +private JsonValue getAJsonChild(JsonObject obj) { result = obj.getPropValue(_) } + module MainModulePath { MainModulePath of(PackageJson pkg) { result.getPackageJson() = pkg } } diff --git a/javascript/ql/lib/semmle/javascript/Paths.qll b/javascript/ql/lib/semmle/javascript/Paths.qll index e22d5ad6132..7574fe1e301 100644 --- a/javascript/ql/lib/semmle/javascript/Paths.qll +++ b/javascript/ql/lib/semmle/javascript/Paths.qll @@ -180,7 +180,7 @@ private Path resolveUpTo(PathString p, int n, Folder root, boolean inTS) { } /** - * Gets the `i`th component of the path `str`, where `base` is the resolved path one level up. + * Gets the `n`th component of the path `str`, where `base` is the resolved path one level up. * Supports that the root directory might be compiled output from TypeScript. * `inTS` is true if the result is TypeScript that is compiled into the path specified by `str`. */ @@ -227,7 +227,7 @@ private module TypeScriptOutDir { } /** - * Gets the `outDir` option from a tsconfig file from the folder `parent`. + * Gets the "outDir" option from a `tsconfig` file from the folder `parent`. */ private string getOutDir(JsonObject tsconfig, Folder parent) { tsconfig.getFile().getBaseName().regexpMatch("tsconfig.*\\.json") and diff --git a/javascript/ql/lib/semmle/javascript/PrintAst.qll b/javascript/ql/lib/semmle/javascript/PrintAst.qll index 3da7eac2de2..672c2ab5a9e 100644 --- a/javascript/ql/lib/semmle/javascript/PrintAst.qll +++ b/javascript/ql/lib/semmle/javascript/PrintAst.qll @@ -195,7 +195,7 @@ private module PrintJavaScript { * Gets the `i`th child of `element`. * Can be overridden in subclasses to get more specific behavior for `getChild()`. */ - AstNode getChildNode(int childIndex) { result = getLocationSortedChild(element, childIndex) } + AstNode getChildNode(int i) { result = getLocationSortedChild(element, i) } } /** Provides predicates for pretty printing `AstNode`s. */ diff --git a/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll b/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll index 5c15ea3d3aa..9d8b3967b1c 100644 --- a/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll +++ b/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll @@ -260,7 +260,7 @@ module RangeAnalysis { } /** - * Holds if the given comparison can be modeled as `A B + bias` where `` is the comparison operator, + * Holds if the given `comparison` can be modeled as `A B + bias` where `` is the comparison operator, * and `A` is `a * asign` and likewise `B` is `b * bsign`. */ predicate linearComparison( @@ -310,18 +310,18 @@ module RangeAnalysis { * Holds if `guard` asserts that the outcome of `A B + bias` is true, where `` is a comparison operator. */ predicate linearComparisonGuard( - ConditionGuardNode guard, DataFlow::Node a, int asign, string operator, DataFlow::Node b, - int bsign, Bias bias + ConditionGuardNode guard, DataFlow::Node a, int asign, string op, DataFlow::Node b, int bsign, + Bias bias ) { exists(Comparison compare | compare = guard.getTest().flow().getImmediatePredecessor*().asExpr() and linearComparison(compare, a, asign, b, bsign, bias) and ( - guard.getOutcome() = true and operator = compare.getOperator() + guard.getOutcome() = true and op = compare.getOperator() or not hasNaNIndicator(guard.getContainer()) and guard.getOutcome() = false and - operator = negateOperator(compare.getOperator()) + op = negateOperator(compare.getOperator()) ) ) } @@ -657,13 +657,13 @@ module RangeAnalysis { */ pragma[noopt] private predicate reachableByNegativeEdges( - DataFlow::Node a, int asign, DataFlow::Node b, int bsign, ControlFlowNode cfg + DataFlow::Node src, int asign, DataFlow::Node dst, int bsign, ControlFlowNode cfg ) { - negativeEdge(a, asign, b, bsign, cfg) + negativeEdge(src, asign, dst, bsign, cfg) or exists(DataFlow::Node mid, int midx, ControlFlowNode midcfg | - reachableByNegativeEdges(a, asign, mid, midx, cfg) and - negativeEdge(mid, midx, b, bsign, midcfg) and + reachableByNegativeEdges(src, asign, mid, midx, cfg) and + negativeEdge(mid, midx, dst, bsign, midcfg) and exists(BasicBlock bb, int i, int j | bb.getNode(i) = midcfg and bb.getNode(j) = cfg and @@ -676,8 +676,8 @@ module RangeAnalysis { DataFlow::Node mid, int midx, ControlFlowNode midcfg, BasicBlock midBB, ReachableBasicBlock midRBB, BasicBlock cfgBB | - reachableByNegativeEdges(a, asign, mid, midx, cfg) and - negativeEdge(mid, midx, b, bsign, midcfg) and + reachableByNegativeEdges(src, asign, mid, midx, cfg) and + negativeEdge(mid, midx, dst, bsign, midcfg) and midBB = midcfg.getBasicBlock() and midRBB = midBB.(ReachableBasicBlock) and cfgBB = cfg.getBasicBlock() and diff --git a/javascript/ql/lib/semmle/javascript/Routing.qll b/javascript/ql/lib/semmle/javascript/Routing.qll index 858ec1ad238..b89a2e257a8 100644 --- a/javascript/ql/lib/semmle/javascript/Routing.qll +++ b/javascript/ql/lib/semmle/javascript/Routing.qll @@ -148,6 +148,18 @@ module Routing { this instanceof MkRouter } + /** + * Like `mayResumeDispatch` but without the assumption that functions with an unknown + * implementation invoke their continuation. + */ + predicate definitelyResumesDispatch() { + this.getLastChild().definitelyResumesDispatch() + or + exists(this.(RouteHandler).getAContinuationInvocation()) + or + this instanceof MkRouter + } + /** Gets the parent of this node, provided that this node may invoke its continuation. */ private Node getContinuationParent() { result = this.getParent() and @@ -229,7 +241,7 @@ module Routing { } /** - * Holds if `node` has processed the incoming request strictly prior to this node. + * Holds if `guard` has processed the incoming request strictly prior to this node. */ pragma[inline] private predicate isGuardedByNodeInternal(Node guard) { diff --git a/javascript/ql/lib/semmle/javascript/SSA.qll b/javascript/ql/lib/semmle/javascript/SSA.qll index 41831a282ac..8e60fb0c3e4 100644 --- a/javascript/ql/lib/semmle/javascript/SSA.qll +++ b/javascript/ql/lib/semmle/javascript/SSA.qll @@ -501,7 +501,7 @@ class SsaExplicitDefinition extends SsaDefinition, TExplicitDef { } /** This SSA definition corresponds to the definition of `v` at `def`. */ - predicate defines(VarDef d, SsaSourceVariable v) { this = TExplicitDef(_, _, d, v) } + predicate defines(VarDef def, SsaSourceVariable v) { this = TExplicitDef(_, _, def, v) } /** Gets the variable definition wrapped by this SSA definition. */ VarDef getDef() { this = TExplicitDef(_, _, result, _) } diff --git a/javascript/ql/lib/semmle/javascript/TypeScript.qll b/javascript/ql/lib/semmle/javascript/TypeScript.qll index 3f27fc0bd7c..51b68220a9e 100644 --- a/javascript/ql/lib/semmle/javascript/TypeScript.qll +++ b/javascript/ql/lib/semmle/javascript/TypeScript.qll @@ -751,7 +751,7 @@ class TypeAccess extends @typeaccess, TypeExpr, TypeRef { } /** - * Gets a suitable name for the library imported by `import`. + * Gets a suitable name for the library imported by `imprt`. * * For relative imports, this is the snapshot-relative path to the imported module. * For non-relative imports, it is the import path itself. diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll index f8625afd25f..19153304811 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll @@ -353,7 +353,7 @@ abstract class BarrierGuardNode extends DataFlow::Node { } /** - * Holds if data flow node `nd` acts as a barrier for data flow. + * Holds if data flow node `guard` acts as a barrier for data flow. * * `label` is bound to the blocked label, or the empty string if all labels should be blocked. */ @@ -382,7 +382,7 @@ private predicate barrierGuardIsRelevant(BarrierGuardNode guard) { } /** - * Holds if data flow node `nd` acts as a barrier for data flow due to aliasing through + * Holds if data flow node `guard` acts as a barrier for data flow due to aliasing through * an access path. * * `label` is bound to the blocked label, or the empty string if all labels should be blocked. @@ -1155,7 +1155,7 @@ private predicate appendStep( } /** - * Holds if a function invoked at `invk` may return an expression into which `input`, + * Holds if a function invoked at `output` may return an expression into which `input`, * which is either an argument or a definition captured by the function, flows under * configuration `cfg`, possibly through callees. */ @@ -1395,7 +1395,7 @@ private predicate reachableFromStoreBase( } /** - * Holds if `base` is the base of a write to property `prop`, and `nd` is reachable + * Holds if `base` is the base of a write to property `endProp`, and `nd` is reachable * from `base` under configuration `cfg` (possibly through callees) along a path whose * last step is summarized by `newSummary`, and the previous steps are summarized * by `oldSummary`. @@ -1758,7 +1758,7 @@ class PathNode extends TPathNode { this = MkSinkNode(nd, cfg) } - /** Holds if this path node wraps data-flow node `nd` and configuration `c`. */ + /** Holds if this path node wraps data-flow node `n` and configuration `c`. */ predicate wraps(DataFlow::Node n, DataFlow::Configuration c) { nd = n and cfg = c } /** Gets the underlying configuration of this path node. */ @@ -1873,7 +1873,7 @@ class MidPathNode extends PathNode, MkMidNode { MidPathNode() { this = MkMidNode(nd, cfg, summary) } - /** Holds if this path node wraps data-flow node `nd`, configuration `c` and summary `s`. */ + /** Holds if this path node wraps data-flow node `n`, configuration `c` and summary `s`. */ predicate wraps(DataFlow::Node n, DataFlow::Configuration c, PathSummary s) { nd = n and cfg = c and summary = s } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll index 36cacb1d7d7..bad3c47ef89 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll @@ -1653,7 +1653,7 @@ module DataFlow { } /** - * Holds if the flow information for this node is incomplete. + * Holds if the flow information for the node `nd`. * * This predicate holds if there may be a source flow node from which data flows into * this node, but that node is not a result of `getALocalSource()` due to analysis incompleteness. diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Portals.qll b/javascript/ql/lib/semmle/javascript/dataflow/Portals.qll index 299819de4cd..3a8e0b477fb 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Portals.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Portals.qll @@ -498,7 +498,7 @@ private module ReturnPortal { invk = callee.getAnExitNode(isRemote).getAnInvocation() } - /** Holds if `ret` is a return node of a function flowing through `callee`. */ + /** Holds if `ret` is a return node of a function flowing through `base`. */ predicate returns(Portal base, DataFlow::Node ret, boolean escapes) { ret = base.getAnEntryNode(escapes).getALocalSource().(DataFlow::FunctionNode).getAReturn() } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll b/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll index 73d3f9c96ac..8813b974add 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll @@ -33,13 +33,7 @@ private import semmle.javascript.internal.CachedStages * import("fs") * ``` */ -class SourceNode extends DataFlow::Node { - SourceNode() { - this instanceof SourceNode::Range - or - none() and this instanceof SourceNode::Internal::RecursionGuard - } - +class SourceNode extends DataFlow::Node instanceof SourceNode::Range { /** * Holds if this node flows into `sink` in zero or more local (that is, * intra-procedural) steps. @@ -340,12 +334,6 @@ module SourceNode { DataFlow::functionReturnNode(this, _) } } - - /** INTERNAL. DO NOT USE. */ - module Internal { - /** An empty class that some tests are using to enforce that SourceNode is non-recursive. */ - abstract class RecursionGuard extends DataFlow::Node { } - } } private class NodeModuleSourcesNodes extends SourceNode::Range { diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll index 6b5604f9135..86fc96a79cc 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/TaintTracking.qll @@ -831,7 +831,7 @@ module TaintTracking { } /** - * Holds if the property `loadStep` should be copied from the object `pred` to the property `storeStep` of object `succ`. + * Holds if the property `loadProp` should be copied from the object `pred` to the property `storeProp` of object `succ`. * * This step is used to copy the value of our pseudo-property that can later be accessed using a `get` or `getAll` call. * For an expression `url.searchParams`, the property `hiddenUrlPseudoProperty()` from the `url` object is stored in the property `getableUrlPseudoProperty()` on `url.searchParams`. diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll b/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll index 896f0177ec3..12aa8d09ed1 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/TypeTracking.qll @@ -312,7 +312,7 @@ class TypeBackTracker extends TTypeBackTracker { * result = < some API call >.getArgument(< n >) * or * exists (DataFlow::TypeBackTracker t2 | - * t = t2.smallstep(result, myType(t2)) + * t2 = t.smallstep(result, myType(t2)) * ) * } * diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll index c2ce840fae2..94a6532b107 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/CallGraphs.qll @@ -190,7 +190,7 @@ module CallGraph { } /** - * Holds if `ref` installs an accessor on an object. Such property writes should not + * Holds if `write` installs an accessor on an object. Such property writes should not * be considered calls to an accessor. */ pragma[nomagic] diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/PropertyTypeInference.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/PropertyTypeInference.qll index 0f6b816daeb..15a4fec7406 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/PropertyTypeInference.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/PropertyTypeInference.qll @@ -10,7 +10,7 @@ private import AbstractPropertiesImpl private import AbstractValuesImpl /** - * Flow analysis for property reads, either explicitly (`x.p` or `x[e]`) or + * An analyzed property read, either explicitly (`x.p` or `x[e]`) or * implicitly. */ abstract class AnalyzedPropertyRead extends DataFlow::AnalyzedNode { @@ -86,7 +86,7 @@ pragma[noinline] private predicate isTrackedPropertyName(string prop) { exists(MkAbstractProperty(_, prop)) } /** - * Flow analysis for property writes, including exports (which are + * An analyzed property write, including exports (which are * modeled as assignments to `module.exports`). */ abstract class AnalyzedPropertyWrite extends DataFlow::Node { diff --git a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll index 56fca49cd10..d379184c11f 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/AngularJSExpressions.qll @@ -15,11 +15,11 @@ import javascript abstract class NgSourceProvider extends Locatable { /** * Holds if this element provides the source as `src` for an AngularJS expression at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. + * The location spans column `startColumn` of line `startLine` to + * column `endColumn` of line `endLine` in file `filepath`. */ abstract predicate providesSourceAt( - string src, string path, int startLine, int startColumn, int endLine, int endColumn + string src, string filepath, int startLine, int startColumn, int endLine, int endColumn ); /** @@ -92,10 +92,10 @@ abstract private class HtmlAttributeAsNgSourceProvider extends NgSourceProvider, endColumn = startColumn + src.length() - 1 } - /** The source code of the expression. */ + /** Gets the source code of the expression. */ abstract string getSource(); - /** The offset into the attribute where the expression starts. */ + /** Gets the offset into the attribute where the expression starts. */ abstract int getOffset(); override DOM::ElementDefinition getEnclosingElement() { result = this.getElement() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll index dcce784cd1a..6d421de851c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/AngularJS/ServiceDefinitions.qll @@ -278,11 +278,11 @@ abstract private class CustomSpecialServiceDefinition extends CustomServiceDefin bindingset[moduleMethodName] private predicate isCustomServiceDefinitionOnModule( DataFlow::CallNode mce, string moduleMethodName, string serviceName, - DataFlow::Node factoryArgument + DataFlow::Node factoryFunction ) { mce = moduleRef(_).getAMethodCall(moduleMethodName) and mce.getArgument(0).asExpr().mayHaveStringValue(serviceName) and - factoryArgument = mce.getArgument(1) + factoryFunction = mce.getArgument(1) } pragma[inline] diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Bundling.qll b/javascript/ql/lib/semmle/javascript/frameworks/Bundling.qll index d7fd51c1b82..1315ac651d5 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Bundling.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Bundling.qll @@ -102,9 +102,9 @@ private predicate isBrowserifyDependencyMap(ObjectExpr deps) { * Holds if `m` is a function that looks like a bundled module created * by Webpack. * - * Parameters must be named either `module` or `exports`, - * or their name must contain the substring `webpack_require` - * or `webpack_module_template_argument`. + * Parameters must be named either "module" or "exports", + * or their name must contain the substring "webpack_require" + * or "webpack_module_template_argument". */ private predicate isWebpackModule(FunctionExpr m) { forex(Parameter parm | parm = m.getAParameter() | diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll b/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll index 9da7c959cb0..489fcf550c4 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll @@ -50,7 +50,7 @@ module ConnectExpressShared { } /** - * Holds if `fun` appears to match the given signature based on parameter naming. + * Holds if `function` appears to match the given signature based on parameter naming. */ private predicate matchesSignature(Function function, RouteHandlerSignature sig) { function.getNumParameter() = sig.getArity() and diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Express.qll b/javascript/ql/lib/semmle/javascript/frameworks/Express.qll index ca9a151e3c6..19616530763 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Express.qll @@ -33,6 +33,11 @@ module Express { or // `app = [new] express.Router()` result = DataFlow::moduleMember("express", "Router").getAnInvocation() + or + exists(DataFlow::SourceNode app | + app.hasUnderlyingType("probot/lib/application", "Application") and + result = app.getAMethodCall("route") + ) } /** @@ -1043,4 +1048,22 @@ module Express { override DataFlow::SourceNode getOutput() { result = this.getCallback(2).getParameter(1) } } + + private class ResumeDispatchRefinement extends Routing::RouteHandler { + ResumeDispatchRefinement() { this.getFunction() instanceof RouteHandler } + + override predicate mayResumeDispatch() { this.getAParameter().getName() = "next" } + + override predicate definitelyResumesDispatch() { this.getAParameter().getName() = "next" } + } + + private class ExpressStaticResumeDispatchRefinement extends Routing::Node { + ExpressStaticResumeDispatchRefinement() { + this = Routing::getNode(DataFlow::moduleMember("express", "static").getACall()) + } + + override predicate mayResumeDispatch() { none() } + + override predicate definitelyResumesDispatch() { none() } + } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll b/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll index 431007cd944..000015191f8 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll @@ -299,7 +299,7 @@ module Fastify { } /** - * Holds if `rh` uses `plugin`. + * Holds if `rh` uses `middleware`. */ private predicate usesMiddleware(RouteHandler rh, DataFlow::SourceNode middleware) { exists(RouteSetup setup | diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Handlebars.qll b/javascript/ql/lib/semmle/javascript/frameworks/Handlebars.qll index 372e0145003..2e376f49ae7 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Handlebars.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Handlebars.qll @@ -61,13 +61,13 @@ private module HandlebarsTaintSteps { * the `FunctionNode` representing `function loudHelper`, and return its parameter `text`. */ private DataFlow::ParameterNode getRegisteredHelperParam( - string helperName, DataFlow::FunctionNode helperFunction, int paramIndex + string helperName, DataFlow::FunctionNode func, int paramIndex ) { exists(DataFlow::CallNode registerHelperCall | registerHelperCall = any(Handlebars::Handlebars hb).getAMemberCall("registerHelper") and registerHelperCall.getArgument(0).mayHaveStringValue(helperName) and - helperFunction = registerHelperCall.getArgument(1).getAFunctionValue() and - result = helperFunction.getParameter(paramIndex) + func = registerHelperCall.getArgument(1).getAFunctionValue() and + result = func.getParameter(paramIndex) ) } @@ -132,7 +132,7 @@ private module HandlebarsTaintSteps { private predicate isHandlebarsArgStep(DataFlow::Node pred, DataFlow::Node succ) { exists( string helperName, DataFlow::CallNode templatingCall, DataFlow::CallNode compileCall, - DataFlow::FunctionNode helperFunction + DataFlow::FunctionNode func | templatingCall = compiledTemplate(compileCall).getACall() and exists(string templateText, string paramName, int argIdx | @@ -140,7 +140,7 @@ private module HandlebarsTaintSteps { | pred = templatingCall.getArgument(0).getALocalSource().getAPropertyWrite(paramName).getRhs() and isTemplateHelperCallArg(templateText, helperName, argIdx, paramName) and - succ = getRegisteredHelperParam(helperName, helperFunction, argIdx) + succ = getRegisteredHelperParam(helperName, func, argIdx) ) ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll index 0d2da4b10bb..45146fd6b9f 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NoSQL.qll @@ -583,11 +583,11 @@ private module Minimongo { */ module CollectionMethodSignatures { /** - * Holds if Collection method `name` interprets parameter `n` as a query. + * Holds if Collection method `name` interprets parameter `queryArgIdx` as a query. */ - predicate interpretsArgumentAsQuery(string m, int queryArgIdx) { + predicate interpretsArgumentAsQuery(string name, int queryArgIdx) { // implements most of the MongoDB interface - MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(m, queryArgIdx) + MongoDB::CollectionMethodSignatures::interpretsArgumentAsQuery(name, queryArgIdx) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll index 75e222730cc..5bd92e3ec5c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll @@ -474,17 +474,17 @@ module NodeJSLib { * that receives the data. * * We determine this by looking for an externs declaration for - * `fs.methodName` where the `i`th parameter's name is `data` or + * `fs.methodName` where the `i`th parameter's name (`paramName`) is `data` or * `buffer` or a `callback`. */ - private predicate fsDataParam(string methodName, int i, string n) { + private predicate fsDataParam(string methodName, int i, string paramName) { exists(ExternalMemberDecl decl, Function f, JSDocParamTag p | decl.hasQualifiedName("fs", methodName) and f = decl.getInit() and p.getDocumentedParameter() = f.getParameter(i).getAVariable() and - n = p.getName().toLowerCase() + paramName = p.getName().toLowerCase() | - n = "data" or n = "buffer" or n = "callback" + paramName = ["data", "buffer", "callback"] ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll b/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll index e48d674fa74..238bcae482a 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/SocketIO.qll @@ -55,7 +55,7 @@ module SocketIO { /** Gets the namespace with the given path of this server. */ NamespaceObject getNamespace(string path) { result = MkNamespace(this, path) } - /** Gets a api node that may refer to the socket.io server created at `srv`. */ + /** Gets a api node that may refer to a socket.io server. */ private API::Node server() { result = node or @@ -144,7 +144,7 @@ module SocketIO { override NamespaceObject getNamespace() { result = ns } /** - * Gets a data flow node that may refer to the socket.io namespace created at `ns`. + * Gets a data flow node that may refer a the socket.io namespace. */ private API::Node namespace() { result = node diff --git a/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll b/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll index cc94c966c32..0f2b36216c9 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/jQuery.qll @@ -309,12 +309,13 @@ private module JQueryClientRequest { /** * Gets a node referring to the response contained in an `jqXHR` object. */ - private DataFlow::SourceNode getAResponseNodeFromAnXHRObject(DataFlow::SourceNode obj) { + private DataFlow::SourceNode getAResponseNodeFromAnXHRObject(DataFlow::SourceNode jqXHR) { result = - obj.getAPropertyRead(any(string s | - s = "responseText" or - s = "responseXML" - )) + jqXHR + .getAPropertyRead(any(string s | + s = "responseText" or + s = "responseXML" + )) } /** diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll index d594da271b8..fd549429e4a 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionCustomizations.qll @@ -80,7 +80,7 @@ module UnsafeHtmlConstruction { t.start() and result = sink or - exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, isUsedInXssSink(t2, sink))) + exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, isUsedInXssSink(t2, sink))) or exists(DataFlow::TypeBackTracker t2 | t.continue() = t2 and diff --git a/javascript/ql/src/CHANGELOG.md b/javascript/ql/src/CHANGELOG.md index 68660fcbb52..00016a45458 100644 --- a/javascript/ql/src/CHANGELOG.md +++ b/javascript/ql/src/CHANGELOG.md @@ -1,3 +1,16 @@ +## 0.3.1 + +### New Queries + +- A new query "Case-sensitive middleware path" (`js/case-sensitive-middleware-path`) has been added. + It highlights middleware routes that can be bypassed due to having a case-sensitive regular expression path. + +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/javascript-all` package. + ## 0.2.0 ### Minor Analysis Improvements diff --git a/javascript/ql/src/Declarations/DeadStoreOfProperty.ql b/javascript/ql/src/Declarations/DeadStoreOfProperty.ql index 48b574f8cd1..c8cb0d8536e 100644 --- a/javascript/ql/src/Declarations/DeadStoreOfProperty.ql +++ b/javascript/ql/src/Declarations/DeadStoreOfProperty.ql @@ -154,7 +154,7 @@ predicate maybeAssignsAccessedPropInBlock(DataFlow::PropWrite assign, boolean af */ private module PurityCheck { /** - * Holds if a ControlFlowNode `c` is before an impure expression inside `bb`. + * Holds if `write` is before an impure expression inside `bb`. */ predicate isBeforeImpure(DataFlow::PropWrite write, ReachableBasicBlock bb) { getANodeAfterWrite(write, bb).(Expr).isImpure() @@ -181,7 +181,7 @@ private module PurityCheck { } /** - * Holds if a ControlFlowNode `c` is after an impure expression inside `bb`. + * Holds if `write` is after an impure expression inside `bb`. */ predicate isAfterImpure(DataFlow::PropWrite write, ReachableBasicBlock bb) { getANodeBeforeWrite(write, bb).(Expr).isImpure() diff --git a/javascript/ql/src/Declarations/UnusedVariable.ql b/javascript/ql/src/Declarations/UnusedVariable.ql index 254c8c206b1..f678c7d5b19 100644 --- a/javascript/ql/src/Declarations/UnusedVariable.ql +++ b/javascript/ql/src/Declarations/UnusedVariable.ql @@ -144,6 +144,9 @@ predicate whitelisted(UnusedLocal v) { // exclude variables mentioned in JSDoc comments in externs mentionedInJSDocComment(v) or + // the attributes in .vue files are not extracted, so we can get false positives in those. + v.getADeclaration().getFile().getExtension() = "vue" + or // exclude variables used to filter out unwanted properties isPropertyFilter(v) or diff --git a/javascript/ql/src/Expressions/CompareIdenticalValues.ql b/javascript/ql/src/Expressions/CompareIdenticalValues.ql index 48eae0c49cd..19e9338fb6f 100644 --- a/javascript/ql/src/Expressions/CompareIdenticalValues.ql +++ b/javascript/ql/src/Expressions/CompareIdenticalValues.ql @@ -38,7 +38,7 @@ predicate accessWithConversions(Expr e, Variable v) { } /** - * A comment containing the word "NaN". + * Holds if `c` is a comment containing the word "NaN". */ predicate isNaNComment(Comment c, string filePath, int startLine) { c.getText().matches("%NaN%") and diff --git a/javascript/ql/src/Expressions/TypoDatabase.qll b/javascript/ql/src/Expressions/TypoDatabase.qll index aad43d9d0cc..fadc1b8af75 100644 --- a/javascript/ql/src/Expressions/TypoDatabase.qll +++ b/javascript/ql/src/Expressions/TypoDatabase.qll @@ -5793,6 +5793,8 @@ predicate typos(string wrong, string right) { or wrong = "paramters" and right = "parameters" or + wrong = "parametarized" and right = "parameterized" + or wrong = "paranthesis" and right = "parenthesis" or wrong = "paraphenalia" and right = "paraphernalia" diff --git a/javascript/ql/src/LanguageFeatures/TemplateSyntaxInStringLiteral.ql b/javascript/ql/src/LanguageFeatures/TemplateSyntaxInStringLiteral.ql index daa34825939..f22b9779560 100644 --- a/javascript/ql/src/LanguageFeatures/TemplateSyntaxInStringLiteral.ql +++ b/javascript/ql/src/LanguageFeatures/TemplateSyntaxInStringLiteral.ql @@ -84,10 +84,10 @@ predicate hasObjectProvidingTemplateVariables(CandidateStringLiteral lit) { * Gets a declaration of variable `v` in `tl`, where `v` has the given `name` and * belongs to `scope`. */ -VarDecl getDeclIn(Variable v, Scope s, string name, CandidateTopLevel tl) { +VarDecl getDeclIn(Variable v, Scope scope, string name, CandidateTopLevel tl) { v.getName() = name and v.getADeclaration() = result and - v.getScope() = s and + v.getScope() = scope and result.getTopLevel() = tl } diff --git a/javascript/ql/src/Metrics/ES20xxFeatures.qll b/javascript/ql/src/Metrics/ES20xxFeatures.qll index 8069ba5e50f..4aaed2d0fda 100644 --- a/javascript/ql/src/Metrics/ES20xxFeatures.qll +++ b/javascript/ql/src/Metrics/ES20xxFeatures.qll @@ -6,7 +6,7 @@ import javascript /** - * Holds if `nd` is a use of a feature introduced in ECMAScript version `ver` + * Holds if `nd` is a use of a feature introduced in ECMAScript `version` * from the given category. * * Categories are taken from Kangax' [ECMAScript 6 compatibility table] diff --git a/javascript/ql/src/NodeJS/InvalidExport.ql b/javascript/ql/src/NodeJS/InvalidExport.ql index 9daa363e888..e0b4a73fd69 100644 --- a/javascript/ql/src/NodeJS/InvalidExport.ql +++ b/javascript/ql/src/NodeJS/InvalidExport.ql @@ -16,14 +16,14 @@ import javascript /** * Holds if `assign` assigns the value of `nd` to `exportsVar`, which is an `exports` variable */ -predicate exportsAssign(Assignment assgn, Variable exportsVar, DataFlow::Node nd) { +predicate exportsAssign(Assignment assign, Variable exportsVar, DataFlow::Node nd) { exists(NodeModule m | exportsVar = m.getScope().getVariable("exports") and - assgn.getLhs() = exportsVar.getAnAccess() and - nd = assgn.getRhs().flow() + assign.getLhs() = exportsVar.getAnAccess() and + nd = assign.getRhs().flow() ) or - exportsAssign(assgn, exportsVar, nd.getASuccessor()) + exportsAssign(assign, exportsVar, nd.getASuccessor()) } /** diff --git a/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql b/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql index e352617337f..06e0a8146ef 100644 --- a/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql +++ b/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql @@ -39,10 +39,10 @@ RegExpTerm getEffectiveRoot(RegExpTerm actualRoot) { /** * Holds if `term` contains an anchor on both ends. */ -predicate isPossiblyAnchoredOnBothEnds(RegExpSequence node) { - node.getAChild*() instanceof RegExpCaret and - node.getAChild*() instanceof RegExpDollar and - node.getNumChild() >= 2 +predicate isPossiblyAnchoredOnBothEnds(RegExpSequence term) { + term.getAChild*() instanceof RegExpCaret and + term.getAChild*() instanceof RegExpDollar and + term.getNumChild() >= 2 } /** diff --git a/javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll b/javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll index 2192951f76d..5e9bc406512 100644 --- a/javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll +++ b/javascript/ql/src/Security/CWE-020/HostnameRegexpShared.qll @@ -53,7 +53,7 @@ predicate matchesBeginningOfString(RegExpTerm term) { } /** - * Holds if the given sequence contains top-level domain preceded by a dot, such as `.com`, + * Holds if the given sequence `seq` contains top-level domain preceded by a dot, such as `.com`, * excluding cases where this is at the very beginning of the regexp. * * `i` is bound to the index of the last child in the top-level domain part. diff --git a/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql b/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql index 90ae904097b..109e63b45b1 100644 --- a/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql +++ b/javascript/ql/src/Security/CWE-020/IncompleteUrlSchemeCheck.ql @@ -109,8 +109,8 @@ DataFlow::Node schemeCheck(DataFlow::Node nd, DangerousScheme scheme) { } /** Gets a data-flow node that checks an instance of `ap` against the given `scheme`. */ -DataFlow::Node schemeCheckOn(DataFlow::SourceNode root, string path, DangerousScheme scheme) { - result = schemeCheck(AccessPath::getAReferenceTo(root, path), scheme) +DataFlow::Node schemeCheckOn(DataFlow::SourceNode root, string ap, DangerousScheme scheme) { + result = schemeCheck(AccessPath::getAReferenceTo(root, ap), scheme) } from DataFlow::SourceNode root, string path, int n diff --git a/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql b/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql index 03a7a75828b..650b71dd62f 100644 --- a/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql +++ b/javascript/ql/src/Security/CWE-020/IncorrectSuffixCheck.ql @@ -84,7 +84,7 @@ class LiteralLengthExpr extends DotExpr { } /** - * Holds if `length` is derived from the length of the given `indexOf`-operand. + * Holds if `length` is derived from the length of the given indexOf `operand`. */ predicate isDerivedFromLength(DataFlow::Node length, DataFlow::Node operand) { exists(IndexOfCall call | operand = call.getAnOperand() | diff --git a/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql b/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql index 886c78b0161..694e827ba41 100644 --- a/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql +++ b/javascript/ql/src/Security/CWE-094/ImproperCodeSanitization.ql @@ -50,7 +50,7 @@ private DataFlow::Node endsInCodeInjectionSink(DataFlow::TypeBackTracker t) { not result instanceof StringOps::ConcatenationRoot // the heuristic CodeInjection sink looks for string-concats, we are not interrested in those here. ) or - exists(DataFlow::TypeBackTracker t2 | t = t2.smallstep(result, endsInCodeInjectionSink(t2))) + exists(DataFlow::TypeBackTracker t2 | t2 = t.smallstep(result, endsInCodeInjectionSink(t2))) } /** diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp new file mode 100644 index 00000000000..96bf1c18c94 --- /dev/null +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.qhelp @@ -0,0 +1,44 @@ + + + + +

    +Using a case-sensitive regular expression path in a middleware route enables an attacker to bypass that middleware +when accessing an endpoint with a case-insensitive path. +Paths specified using a string are case-insensitive, whereas regular expressions are case-sensitive by default. +

    +
    + + +

    +When using a regular expression as a middleware path, make sure the regular expression is +case-insensitive by adding the i flag. +

    +
    + + +

    +The following example restricts access to paths in the /admin path to users logged in as +administrators: +

    + +

    +A path such as /admin/users/45 can only be accessed by an administrator. However, the path +/ADMIN/USERS/45 can be accessed by anyone because the upper-case path doesn't match the case-sensitive regular expression, whereas +Express considers it to match the path string /admin/users. +

    +

    +The issue can be fixed by adding the i flag to the regular expression: +

    + +
    + + +
  • +MDN +Regular Expression Flags. +
  • + + diff --git a/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql new file mode 100644 index 00000000000..ccf659bf024 --- /dev/null +++ b/javascript/ql/src/Security/CWE-178/CaseSensitiveMiddlewarePath.ql @@ -0,0 +1,121 @@ +/** + * @name Case-sensitive middleware path + * @description Middleware with case-sensitive paths do not protect endpoints with case-insensitive paths. + * @kind problem + * @problem.severity warning + * @security-severity 7.3 + * @precision high + * @id js/case-sensitive-middleware-path + * @tags security + * external/cwe/cwe-178 + */ + +import javascript + +/** + * Converts `s` to upper case, or to lower-case if it was already upper case. + */ +bindingset[s] +string toOtherCase(string s) { + if s.regexpMatch(".*[a-z].*") then result = s.toUpperCase() else result = s.toLowerCase() +} + +RegExpCharacterClass getEnclosingClass(RegExpTerm term) { + term = result.getAChild() + or + term = result.getAChild().(RegExpRange).getAChild() +} + +/** + * Holds if `term` seems to distinguish between upper and lower case letters, assuming the `i` flag is not present. + */ +pragma[inline] +predicate isLikelyCaseSensitiveRegExp(RegExpTerm term) { + exists(RegExpConstant const | + const = term.getAChild*() and + const.getValue().regexpMatch(".*[a-zA-Z].*") and + not getEnclosingClass(const).getAChild().(RegExpConstant).getValue() = + toOtherCase(const.getValue()) and + not const.getParent*() instanceof RegExpNegativeLookahead and + not const.getParent*() instanceof RegExpNegativeLookbehind + ) +} + +/** + * Gets a string matched by `term`, or part of such a string. + */ +string getExampleString(RegExpTerm term) { + result = term.getAMatchedString() + or + // getAMatchedString does not recurse into sequences. Perform one step manually. + exists(RegExpSequence seq | seq = term | + result = + strictconcat(RegExpTerm child, int i, string text | + child = seq.getChild(i) and + ( + text = child.getAMatchedString() + or + not exists(child.getAMatchedString()) and + text = "" + ) + | + text order by i + ) + ) +} + +string getCaseSensitiveBypassExample(RegExpTerm term) { + exists(string example | + example = getExampleString(term) and + result = toOtherCase(example) and + result != example // getting an example string is approximate; ensure we got a proper case-change example + ) +} + +/** + * Holds if `setup` has a path-argument `arg` referring to the given case-sensitive `regexp`. + */ +predicate isCaseSensitiveMiddleware( + Routing::RouteSetup setup, DataFlow::RegExpCreationNode regexp, DataFlow::Node arg +) { + exists(DataFlow::MethodCallNode call | + setup = Routing::getRouteSetupNode(call) and + ( + setup.definitelyResumesDispatch() + or + // If applied to all HTTP methods, be a bit more lenient in detecting middleware + setup.mayResumeDispatch() and + not exists(setup.getOwnHttpMethod()) + ) and + arg = call.getArgument(0) and + regexp.getAReference().flowsTo(arg) and + isLikelyCaseSensitiveRegExp(regexp.getRoot()) and + exists(string flags | + flags = regexp.getFlags() and + not RegExp::isIgnoreCase(flags) + ) + ) +} + +predicate isGuardedCaseInsensitiveEndpoint( + Routing::RouteSetup endpoint, Routing::RouteSetup middleware +) { + isCaseSensitiveMiddleware(middleware, _, _) and + exists(DataFlow::MethodCallNode call | + endpoint = Routing::getRouteSetupNode(call) and + endpoint.isGuardedByNode(middleware) and + call.getArgument(0).mayHaveStringValue(_) + ) +} + +from + DataFlow::RegExpCreationNode regexp, Routing::RouteSetup middleware, Routing::RouteSetup endpoint, + DataFlow::Node arg, string example +where + isCaseSensitiveMiddleware(middleware, regexp, arg) and + example = getCaseSensitiveBypassExample(regexp.getRoot()) and + isGuardedCaseInsensitiveEndpoint(endpoint, middleware) and + exists(endpoint.getRelativePath().toLowerCase().indexOf(example.toLowerCase())) +select arg, + "This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '" + + example + "' will bypass the middleware.", regexp, "pattern", endpoint, "here" diff --git a/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePath.js b/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePath.js new file mode 100644 index 00000000000..3ae6e071bdd --- /dev/null +++ b/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePath.js @@ -0,0 +1,13 @@ +const app = require('express')(); + +app.use(/\/admin\/.*/, (req, res, next) => { + if (!req.user.isAdmin) { + res.status(401).send('Unauthorized'); + } else { + next(); + } +}); + +app.get('/admin/users/:id', (req, res) => { + res.send(app.database.users[req.params.id]); +}); diff --git a/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePathGood.js b/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePathGood.js new file mode 100644 index 00000000000..1c803bf795d --- /dev/null +++ b/javascript/ql/src/Security/CWE-178/examples/CaseSensitiveMiddlewarePathGood.js @@ -0,0 +1,13 @@ +const app = require('express')(); + +app.use(/\/admin\/.*/i, (req, res, next) => { + if (!req.user.isAdmin) { + res.status(401).send('Unauthorized'); + } else { + next(); + } +}); + +app.get('/admin/users/:id', (req, res) => { + res.send(app.database.users[req.params.id]); +}); diff --git a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql index 99ca8cba8a7..73a20ce2249 100644 --- a/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql +++ b/javascript/ql/src/Security/CWE-200/PrivateFileExposure.ql @@ -72,11 +72,11 @@ pragma[noinline] Folder getAPackageJsonFolder() { result = any(PackageJson json).getFile().getParentContainer() } /** - * Gets a reference to `dirname`, the home folder, the current working folder, or the root folder. + * Gets a reference to a directory that has a `package.json` in the same folder, the home folder, + * the current working folder, or the root folder. * All of these might cause information to be leaked. * - * For `dirname` that can happen if there is a `package.json` file in the same folder. - * It is assumed that the presence of a `package.json` file means that a `node_modules` folder can also exist. + * For the first case it is assumed that the presence of a `package.json` file means that a "node_modules" folder can also exist. * * For the root/home/working folder, they contain so much information that they must leak information somehow (e.g. ssh keys in the `~/.ssh` folder). */ @@ -108,7 +108,7 @@ DataFlow::Node getALeakingFolder(string description) { } /** - * Gets a data-flow node that represents a path to the private folder `path`. + * Gets a data-flow node that represents the private folder descriped by `description`. */ DataFlow::Node getAPrivateFolderPath(string description) { exists(string path | @@ -119,7 +119,7 @@ DataFlow::Node getAPrivateFolderPath(string description) { } /** - * Gest a call that serves the folder `path` to the public. + * Gest a call that serves the folder descriped by `description` to the public. */ DataFlow::CallNode servesAPrivateFolder(string description) { result = DataFlow::moduleMember(["express", "connect"], "static").getACall() and diff --git a/javascript/ql/src/Security/CWE-384/SessionFixation.ql b/javascript/ql/src/Security/CWE-384/SessionFixation.ql index 85d6e69a02f..aca8fbe9e6b 100644 --- a/javascript/ql/src/Security/CWE-384/SessionFixation.ql +++ b/javascript/ql/src/Security/CWE-384/SessionFixation.ql @@ -34,7 +34,7 @@ predicate isLoginSetup(Express::RouteSetup setup) { } /** - * Holds if `handler` regenerates its session using `req.session.regenerate`. + * Holds if `setup` regenerates its session using `req.session.regenerate`. */ pragma[inline] predicate regeneratesSession(Express::RouteSetup setup) { diff --git a/javascript/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/javascript/ql/src/change-notes/released/0.3.0.md similarity index 78% rename from javascript/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename to javascript/ql/src/change-notes/released/0.3.0.md index ff190788cd4..13b4541cd4b 100644 --- a/javascript/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ b/javascript/ql/src/change-notes/released/0.3.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 0.3.0 + +### Breaking Changes + * Contextual queries and the query libraries they depend on have been moved to the `codeql/javascript-all` package. diff --git a/javascript/ql/src/change-notes/released/0.3.1.md b/javascript/ql/src/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..8fe1aaaf4ef --- /dev/null +++ b/javascript/ql/src/change-notes/released/0.3.1.md @@ -0,0 +1,6 @@ +## 0.3.1 + +### New Queries + +- A new query "Case-sensitive middleware path" (`js/case-sensitive-middleware-path`) has been added. + It highlights middleware routes that can be bypassed due to having a case-sensitive regular expression path. diff --git a/javascript/ql/src/codeql-pack.release.yml b/javascript/ql/src/codeql-pack.release.yml index 5274e27ed52..bb106b1cb63 100644 --- a/javascript/ql/src/codeql-pack.release.yml +++ b/javascript/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.1 diff --git a/javascript/ql/lib/definitions.ql b/javascript/ql/src/definitions.ql similarity index 100% rename from javascript/ql/lib/definitions.ql rename to javascript/ql/src/definitions.ql diff --git a/javascript/ql/src/external/DefectFilter.qll b/javascript/ql/src/external/DefectFilter.qll index 40c9527e96d..558d5ef77b6 100644 --- a/javascript/ql/src/external/DefectFilter.qll +++ b/javascript/ql/src/external/DefectFilter.qll @@ -5,8 +5,8 @@ import semmle.javascript.Files /** * Holds if `id` in the opaque identifier of a result reported by query `queryPath`, * such that `message` is the associated message and the location of the result spans - * column `startcolumn` of line `startline` to column `endcolumn` of line `endline` - * in file `filepath`. + * column `startcol` of line `startline` to column `endcol` of line `endline` + * in `file`. * * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ diff --git a/javascript/ql/src/external/MetricFilter.qll b/javascript/ql/src/external/MetricFilter.qll index a857a4fad3e..f195060b60c 100644 --- a/javascript/ql/src/external/MetricFilter.qll +++ b/javascript/ql/src/external/MetricFilter.qll @@ -5,8 +5,8 @@ import javascript /** * Holds if `id` in the opaque identifier of a result reported by query `queryPath`, * such that `value` is the reported metric value and the location of the result spans - * column `startcolumn` of line `startline` to column `endcolumn` of line `endline` - * in file `filepath`. + * column `startcol` of line `startline` to column `endcol` of line `endline` + * in `file`. * * For more information, see [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index 4f54f7dc5bc..9852441a368 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-queries -version: 0.2.1-dev +version: 0.3.2-dev groups: - javascript - queries diff --git a/javascript/ql/test/library-tests/Arrays/TaintFlow.expected b/javascript/ql/test/library-tests/Arrays/TaintFlow.expected new file mode 100644 index 00000000000..20dbaa46ae2 --- /dev/null +++ b/javascript/ql/test/library-tests/Arrays/TaintFlow.expected @@ -0,0 +1,26 @@ +| arrays.js:2:16:2:23 | "source" | arrays.js:5:8:5:14 | obj.foo | +| arrays.js:2:16:2:23 | "source" | arrays.js:11:10:11:15 | arr[i] | +| arrays.js:2:16:2:23 | "source" | arrays.js:15:27:15:27 | e | +| arrays.js:2:16:2:23 | "source" | arrays.js:16:23:16:23 | e | +| arrays.js:2:16:2:23 | "source" | arrays.js:20:8:20:16 | arr.pop() | +| arrays.js:2:16:2:23 | "source" | arrays.js:49:8:49:13 | arr[0] | +| arrays.js:2:16:2:23 | "source" | arrays.js:52:10:52:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:56:10:56:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:60:10:60:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:66:10:66:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:71:10:71:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:74:8:74:29 | arr.fin ... llback) | +| arrays.js:2:16:2:23 | "source" | arrays.js:77:8:77:35 | arrayFi ... llback) | +| arrays.js:2:16:2:23 | "source" | arrays.js:81:10:81:10 | x | +| arrays.js:2:16:2:23 | "source" | arrays.js:84:8:84:17 | arr.at(-1) | +| arrays.js:18:22:18:29 | "source" | arrays.js:18:50:18:50 | e | +| arrays.js:22:15:22:22 | "source" | arrays.js:23:8:23:17 | arr2.pop() | +| arrays.js:25:15:25:22 | "source" | arrays.js:26:8:26:17 | arr3.pop() | +| arrays.js:29:21:29:28 | "source" | arrays.js:30:8:30:17 | arr4.pop() | +| arrays.js:29:21:29:28 | "source" | arrays.js:33:8:33:17 | arr5.pop() | +| arrays.js:29:21:29:28 | "source" | arrays.js:35:8:35:26 | arr5.slice(2).pop() | +| arrays.js:29:21:29:28 | "source" | arrays.js:41:8:41:17 | arr6.pop() | +| arrays.js:44:4:44:11 | "source" | arrays.js:45:10:45:18 | ary.pop() | +| arrays.js:44:4:44:11 | "source" | arrays.js:46:10:46:12 | ary | +| arrays.js:86:9:86:16 | "source" | arrays.js:86:8:86:34 | ["sourc ... ) => x) | +| arrays.js:87:9:87:16 | "source" | arrays.js:87:8:87:36 | ["sourc ... => !!x) | diff --git a/javascript/ql/test/library-tests/Arrays/TaintFlow.ql b/javascript/ql/test/library-tests/Arrays/TaintFlow.ql new file mode 100644 index 00000000000..cee2f294a34 --- /dev/null +++ b/javascript/ql/test/library-tests/Arrays/TaintFlow.ql @@ -0,0 +1,15 @@ +import javascript + +class ArrayTaintFlowConfig extends TaintTracking::Configuration { + ArrayTaintFlowConfig() { this = "ArrayTaintFlowConfig" } + + override predicate isSource(DataFlow::Node source) { source.asExpr().getStringValue() = "source" } + + override predicate isSink(DataFlow::Node sink) { + sink = any(DataFlow::CallNode call | call.getCalleeName() = "sink").getAnArgument() + } +} + +from ArrayTaintFlowConfig config, DataFlow::Node src, DataFlow::Node snk +where config.hasFlow(src, snk) +select src, snk diff --git a/javascript/ql/test/library-tests/Arrays/arrays.js b/javascript/ql/test/library-tests/Arrays/arrays.js index 2dfb203cd54..921f8730738 100644 --- a/javascript/ql/test/library-tests/Arrays/arrays.js +++ b/javascript/ql/test/library-tests/Arrays/arrays.js @@ -82,4 +82,7 @@ } sink(arr.at(-1)); // NOT OK + + sink(["source"].filter((x) => x)); // NOT OK + sink(["source"].filter((x) => !!x)); // NOT OK }); diff --git a/javascript/ql/test/library-tests/Arrays/printAst.expected b/javascript/ql/test/library-tests/Arrays/printAst.expected index c6e097c770e..46b124d85c3 100644 --- a/javascript/ql/test/library-tests/Arrays/printAst.expected +++ b/javascript/ql/test/library-tests/Arrays/printAst.expected @@ -1,9 +1,9 @@ nodes -| arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | semmle.label | [ParExpr] (functi ... T OK }) | -| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | semmle.label | [ExprStmt] (functi ... OK }); | -| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | semmle.order | 1 | -| arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | semmle.label | [FunctionExpr] functio ... OT OK } | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | semmle.label | [BlockStmt] { let ... OT OK } | +| arrays.js:1:1:88:2 | [ParExpr] (functi ... T OK }) | semmle.label | [ParExpr] (functi ... T OK }) | +| arrays.js:1:1:88:3 | [ExprStmt] (functi ... OK }); | semmle.label | [ExprStmt] (functi ... OK }); | +| arrays.js:1:1:88:3 | [ExprStmt] (functi ... OK }); | semmle.order | 1 | +| arrays.js:1:2:88:1 | [FunctionExpr] functio ... OT OK } | semmle.label | [FunctionExpr] functio ... OT OK } | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | semmle.label | [BlockStmt] { let ... OT OK } | | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | [DeclStmt] let source = ... | | arrays.js:2:7:2:12 | [VarDecl] source | semmle.label | [VarDecl] source | | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.label | [VariableDeclarator] source = "source" | @@ -348,6 +348,30 @@ nodes | arrays.js:84:12:84:13 | [Label] at | semmle.label | [Label] at | | arrays.js:84:15:84:16 | [UnaryExpr] -1 | semmle.label | [UnaryExpr] -1 | | arrays.js:84:16:84:16 | [Literal] 1 | semmle.label | [Literal] 1 | +| arrays.js:86:3:86:6 | [VarRef] sink | semmle.label | [VarRef] sink | +| arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | semmle.label | [CallExpr] sink([" ... => x)) | +| arrays.js:86:3:86:36 | [ExprStmt] sink([" ... => x)); | semmle.label | [ExprStmt] sink([" ... => x)); | +| arrays.js:86:8:86:17 | [ArrayExpr] ["source"] | semmle.label | [ArrayExpr] ["source"] | +| arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | semmle.label | [DotExpr] ["source"].filter | +| arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | semmle.label | [MethodCallExpr] ["sourc ... ) => x) | +| arrays.js:86:9:86:16 | [Literal] "source" | semmle.label | [Literal] "source" | +| arrays.js:86:19:86:24 | [Label] filter | semmle.label | [Label] filter | +| arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | semmle.label | [ArrowFunctionExpr] (x) => x | +| arrays.js:86:27:86:27 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | +| arrays.js:86:33:86:33 | [VarRef] x | semmle.label | [VarRef] x | +| arrays.js:87:3:87:6 | [VarRef] sink | semmle.label | [VarRef] sink | +| arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | semmle.label | [CallExpr] sink([" ... > !!x)) | +| arrays.js:87:3:87:38 | [ExprStmt] sink([" ... !!x)); | semmle.label | [ExprStmt] sink([" ... !!x)); | +| arrays.js:87:8:87:17 | [ArrayExpr] ["source"] | semmle.label | [ArrayExpr] ["source"] | +| arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | semmle.label | [DotExpr] ["source"].filter | +| arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | semmle.label | [MethodCallExpr] ["sourc ... => !!x) | +| arrays.js:87:9:87:16 | [Literal] "source" | semmle.label | [Literal] "source" | +| arrays.js:87:19:87:24 | [Label] filter | semmle.label | [Label] filter | +| arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | semmle.label | [ArrowFunctionExpr] (x) => !!x | +| arrays.js:87:27:87:27 | [SimpleParameter] x | semmle.label | [SimpleParameter] x | +| arrays.js:87:33:87:35 | [UnaryExpr] !!x | semmle.label | [UnaryExpr] !!x | +| arrays.js:87:34:87:35 | [UnaryExpr] !x | semmle.label | [UnaryExpr] !x | +| arrays.js:87:35:87:35 | [VarRef] x | semmle.label | [VarRef] x | | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | @@ -391,96 +415,106 @@ nodes | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | | file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Arguments) | semmle.label | (Arguments) | +| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | +| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | | file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) | edges -| arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | semmle.label | 1 | -| arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | semmle.order | 1 | -| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | semmle.label | 1 | -| arrays.js:1:1:85:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:85:2 | [ParExpr] (functi ... T OK }) | semmle.order | 1 | -| arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | semmle.label | 5 | -| arrays.js:1:2:85:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | semmle.order | 5 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | 1 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.order | 1 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.label | 2 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.order | 2 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.label | 3 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.order | 3 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.label | 4 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.order | 4 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.label | 5 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.order | 5 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.label | 6 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.order | 6 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.label | 7 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.order | 7 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.label | 8 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.order | 8 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.label | 9 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.order | 9 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.label | 10 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.order | 10 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.label | 11 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.order | 11 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.label | 12 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.order | 12 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.label | 13 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.order | 13 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.label | 14 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.order | 14 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.label | 15 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.order | 15 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.label | 16 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.order | 16 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.label | 17 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.order | 17 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.label | 18 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.order | 18 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.label | 19 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.order | 19 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.label | 20 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.order | 20 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.label | 21 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.order | 21 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.label | 22 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.order | 22 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.label | 23 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.order | 23 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.label | 24 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.order | 24 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.label | 25 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.order | 25 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.label | 26 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.order | 26 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.label | 27 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.order | 27 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.label | 28 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.order | 28 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.label | 29 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.order | 29 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.label | 30 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.order | 30 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.label | 31 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.order | 31 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.label | 32 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.order | 32 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.label | 33 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.order | 33 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.label | 34 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.order | 34 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.label | 35 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.order | 35 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.label | 36 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.order | 36 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.label | 37 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.order | 37 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.label | 38 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.order | 38 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.label | 39 | -| arrays.js:1:14:85:1 | [BlockStmt] { let ... OT OK } | arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.order | 39 | +| arrays.js:1:1:88:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:88:1 | [FunctionExpr] functio ... OT OK } | semmle.label | 1 | +| arrays.js:1:1:88:2 | [ParExpr] (functi ... T OK }) | arrays.js:1:2:88:1 | [FunctionExpr] functio ... OT OK } | semmle.order | 1 | +| arrays.js:1:1:88:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:88:2 | [ParExpr] (functi ... T OK }) | semmle.label | 1 | +| arrays.js:1:1:88:3 | [ExprStmt] (functi ... OK }); | arrays.js:1:1:88:2 | [ParExpr] (functi ... T OK }) | semmle.order | 1 | +| arrays.js:1:2:88:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | semmle.label | 5 | +| arrays.js:1:2:88:1 | [FunctionExpr] functio ... OT OK } | arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | semmle.order | 5 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.label | 1 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | semmle.order | 1 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.label | 2 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:4:3:4:28 | [DeclStmt] var obj = ... | semmle.order | 2 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.label | 3 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:5:3:5:16 | [ExprStmt] sink(obj.foo); | semmle.order | 3 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.label | 4 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:7:3:7:15 | [DeclStmt] var arr = ... | semmle.order | 4 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.label | 5 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:8:3:8:19 | [ExprStmt] arr.push(source); | semmle.order | 5 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.label | 6 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:10:3:12:3 | [ForStmt] for (va ... OK } | semmle.order | 6 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.label | 7 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:15:3:15:30 | [ExprStmt] arr.for ... nk(e)); | semmle.order | 7 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.label | 8 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:16:3:16:26 | [ExprStmt] arr.map ... nk(e)); | semmle.order | 8 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.label | 9 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:18:3:18:53 | [ExprStmt] [1, 2, ... nk(e)); | semmle.order | 9 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.label | 10 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:20:3:20:18 | [ExprStmt] sink(arr.pop()); | semmle.order | 10 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.label | 11 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:22:3:22:24 | [DeclStmt] var arr2 = ... | semmle.order | 11 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.label | 12 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:23:3:23:19 | [ExprStmt] sink(arr2.pop()); | semmle.order | 12 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.label | 13 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:25:3:25:24 | [DeclStmt] var arr3 = ... | semmle.order | 13 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.label | 14 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:26:3:26:19 | [ExprStmt] sink(arr3.pop()); | semmle.order | 14 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.label | 15 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:28:3:28:16 | [DeclStmt] var arr4 = ... | semmle.order | 15 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.label | 16 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:29:3:29:30 | [ExprStmt] arr4.sp ... urce"); | semmle.order | 16 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.label | 17 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:30:3:30:19 | [ExprStmt] sink(arr4.pop()); | semmle.order | 17 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.label | 18 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:32:3:32:29 | [DeclStmt] var arr5 = ... | semmle.order | 18 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.label | 19 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:33:3:33:19 | [ExprStmt] sink(arr5.pop()); | semmle.order | 19 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.label | 20 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:35:3:35:28 | [ExprStmt] sink(ar ... pop()); | semmle.order | 20 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.label | 21 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:37:3:37:16 | [DeclStmt] var arr6 = ... | semmle.order | 21 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.label | 22 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:38:3:40:3 | [ForStmt] for (va ... i]; } | semmle.order | 22 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.label | 23 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:41:3:41:19 | [ExprStmt] sink(arr6.pop()); | semmle.order | 23 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.label | 24 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:44:3:47:5 | [ExprStmt] ["sourc ... . }); | semmle.order | 24 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.label | 25 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:49:3:49:15 | [ExprStmt] sink(arr[0]); | semmle.order | 25 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.label | 26 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:51:3:53:3 | [ForOfStmt] for (co ... OK } | semmle.order | 26 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.label | 27 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:55:3:57:3 | [ForOfStmt] for (co ... OK } | semmle.order | 27 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.label | 28 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:59:3:61:3 | [ForOfStmt] for (co ... OK } | semmle.order | 28 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.label | 29 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:63:3:63:16 | [DeclStmt] var arr7 = ... | semmle.order | 29 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.label | 30 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:64:3:64:20 | [ExprStmt] arr7.push(...arr); | semmle.order | 30 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.label | 31 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:65:3:67:3 | [ForOfStmt] for (co ... OK } | semmle.order | 31 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.label | 32 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:69:3:69:42 | [DeclStmt] const arrayFrom = ... | semmle.order | 32 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.label | 33 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:70:3:72:3 | [ForOfStmt] for (co ... OK } | semmle.order | 33 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.label | 34 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:74:3:74:31 | [ExprStmt] sink(ar ... back)); | semmle.order | 34 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.label | 35 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:76:3:76:42 | [DeclStmt] const arrayFind = ... | semmle.order | 35 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.label | 36 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:77:3:77:37 | [ExprStmt] sink(ar ... back)); | semmle.order | 36 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.label | 37 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:79:3:79:31 | [DeclStmt] const uniq = ... | semmle.order | 37 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.label | 38 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:80:3:82:3 | [ForOfStmt] for (co ... OK } | semmle.order | 38 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.label | 39 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:84:3:84:19 | [ExprStmt] sink(arr.at(-1)); | semmle.order | 39 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:86:3:86:36 | [ExprStmt] sink([" ... => x)); | semmle.label | 40 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:86:3:86:36 | [ExprStmt] sink([" ... => x)); | semmle.order | 40 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:87:3:87:38 | [ExprStmt] sink([" ... !!x)); | semmle.label | 41 | +| arrays.js:1:14:88:1 | [BlockStmt] { let ... OT OK } | arrays.js:87:3:87:38 | [ExprStmt] sink([" ... !!x)); | semmle.order | 41 | | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.label | 1 | | arrays.js:2:3:2:24 | [DeclStmt] let source = ... | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | semmle.order | 1 | | arrays.js:2:7:2:23 | [VariableDeclarator] source = "source" | arrays.js:2:7:2:12 | [VarDecl] source | semmle.label | 1 | @@ -1081,6 +1115,50 @@ edges | arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | | arrays.js:84:15:84:16 | [UnaryExpr] -1 | arrays.js:84:16:84:16 | [Literal] 1 | semmle.label | 1 | | arrays.js:84:15:84:16 | [UnaryExpr] -1 | arrays.js:84:16:84:16 | [Literal] 1 | semmle.order | 1 | +| arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | arrays.js:86:3:86:6 | [VarRef] sink | semmle.label | 0 | +| arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | arrays.js:86:3:86:6 | [VarRef] sink | semmle.order | 0 | +| arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| arrays.js:86:3:86:36 | [ExprStmt] sink([" ... => x)); | arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | semmle.label | 1 | +| arrays.js:86:3:86:36 | [ExprStmt] sink([" ... => x)); | arrays.js:86:3:86:35 | [CallExpr] sink([" ... => x)) | semmle.order | 1 | +| arrays.js:86:8:86:17 | [ArrayExpr] ["source"] | arrays.js:86:9:86:16 | [Literal] "source" | semmle.label | 1 | +| arrays.js:86:8:86:17 | [ArrayExpr] ["source"] | arrays.js:86:9:86:16 | [Literal] "source" | semmle.order | 1 | +| arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | arrays.js:86:8:86:17 | [ArrayExpr] ["source"] | semmle.label | 1 | +| arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | arrays.js:86:8:86:17 | [ArrayExpr] ["source"] | semmle.order | 1 | +| arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | arrays.js:86:19:86:24 | [Label] filter | semmle.label | 2 | +| arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | arrays.js:86:19:86:24 | [Label] filter | semmle.order | 2 | +| arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | semmle.label | 0 | +| arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | arrays.js:86:8:86:24 | [DotExpr] ["source"].filter | semmle.order | 0 | +| arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | arrays.js:86:33:86:33 | [VarRef] x | semmle.label | 5 | +| arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | arrays.js:86:33:86:33 | [VarRef] x | semmle.order | 5 | +| arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | arrays.js:87:3:87:6 | [VarRef] sink | semmle.label | 0 | +| arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | arrays.js:87:3:87:6 | [VarRef] sink | semmle.order | 0 | +| arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| arrays.js:87:3:87:38 | [ExprStmt] sink([" ... !!x)); | arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | semmle.label | 1 | +| arrays.js:87:3:87:38 | [ExprStmt] sink([" ... !!x)); | arrays.js:87:3:87:37 | [CallExpr] sink([" ... > !!x)) | semmle.order | 1 | +| arrays.js:87:8:87:17 | [ArrayExpr] ["source"] | arrays.js:87:9:87:16 | [Literal] "source" | semmle.label | 1 | +| arrays.js:87:8:87:17 | [ArrayExpr] ["source"] | arrays.js:87:9:87:16 | [Literal] "source" | semmle.order | 1 | +| arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | arrays.js:87:8:87:17 | [ArrayExpr] ["source"] | semmle.label | 1 | +| arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | arrays.js:87:8:87:17 | [ArrayExpr] ["source"] | semmle.order | 1 | +| arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | arrays.js:87:19:87:24 | [Label] filter | semmle.label | 2 | +| arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | arrays.js:87:19:87:24 | [Label] filter | semmle.order | 2 | +| arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | semmle.label | 0 | +| arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | arrays.js:87:8:87:24 | [DotExpr] ["source"].filter | semmle.order | 0 | +| arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | file://:0:0:0:0 | (Arguments) | semmle.label | 1 | +| arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | file://:0:0:0:0 | (Arguments) | semmle.order | 1 | +| arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | arrays.js:87:33:87:35 | [UnaryExpr] !!x | semmle.label | 5 | +| arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | arrays.js:87:33:87:35 | [UnaryExpr] !!x | semmle.order | 5 | +| arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | file://:0:0:0:0 | (Parameters) | semmle.label | 1 | +| arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | file://:0:0:0:0 | (Parameters) | semmle.order | 1 | +| arrays.js:87:33:87:35 | [UnaryExpr] !!x | arrays.js:87:34:87:35 | [UnaryExpr] !x | semmle.label | 1 | +| arrays.js:87:33:87:35 | [UnaryExpr] !!x | arrays.js:87:34:87:35 | [UnaryExpr] !x | semmle.order | 1 | +| arrays.js:87:34:87:35 | [UnaryExpr] !x | arrays.js:87:35:87:35 | [VarRef] x | semmle.label | 1 | +| arrays.js:87:34:87:35 | [UnaryExpr] !x | arrays.js:87:35:87:35 | [VarRef] x | semmle.order | 1 | | file://:0:0:0:0 | (Arguments) | arrays.js:5:8:5:14 | [DotExpr] obj.foo | semmle.label | 0 | | file://:0:0:0:0 | (Arguments) | arrays.js:5:8:5:14 | [DotExpr] obj.foo | semmle.order | 0 | | file://:0:0:0:0 | (Arguments) | arrays.js:8:12:8:17 | [VarRef] source | semmle.label | 0 | @@ -1173,6 +1251,14 @@ edges | file://:0:0:0:0 | (Arguments) | arrays.js:84:8:84:17 | [MethodCallExpr] arr.at(-1) | semmle.order | 0 | | file://:0:0:0:0 | (Arguments) | arrays.js:84:15:84:16 | [UnaryExpr] -1 | semmle.label | 0 | | file://:0:0:0:0 | (Arguments) | arrays.js:84:15:84:16 | [UnaryExpr] -1 | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:86:8:86:34 | [MethodCallExpr] ["sourc ... ) => x) | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:86:26:86:33 | [ArrowFunctionExpr] (x) => x | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:87:8:87:36 | [MethodCallExpr] ["sourc ... => !!x) | semmle.order | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | semmle.label | 0 | +| file://:0:0:0:0 | (Arguments) | arrays.js:87:26:87:35 | [ArrowFunctionExpr] (x) => !!x | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | arrays.js:15:16:15:16 | [SimpleParameter] e | semmle.label | 0 | | file://:0:0:0:0 | (Parameters) | arrays.js:15:16:15:16 | [SimpleParameter] e | semmle.order | 0 | | file://:0:0:0:0 | (Parameters) | arrays.js:16:12:16:12 | [SimpleParameter] e | semmle.label | 0 | @@ -1187,5 +1273,9 @@ edges | file://:0:0:0:0 | (Parameters) | arrays.js:44:26:44:26 | [SimpleParameter] i | semmle.order | 1 | | file://:0:0:0:0 | (Parameters) | arrays.js:44:29:44:31 | [SimpleParameter] ary | semmle.label | 2 | | file://:0:0:0:0 | (Parameters) | arrays.js:44:29:44:31 | [SimpleParameter] ary | semmle.order | 2 | +| file://:0:0:0:0 | (Parameters) | arrays.js:86:27:86:27 | [SimpleParameter] x | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | arrays.js:86:27:86:27 | [SimpleParameter] x | semmle.order | 0 | +| file://:0:0:0:0 | (Parameters) | arrays.js:87:27:87:27 | [SimpleParameter] x | semmle.label | 0 | +| file://:0:0:0:0 | (Parameters) | arrays.js:87:27:87:27 | [SimpleParameter] x | semmle.order | 0 | graphProperties | semmle.graphKind | tree | diff --git a/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.expected b/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.expected deleted file mode 100644 index 59f6fd6e79b..00000000000 --- a/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.expected +++ /dev/null @@ -1 +0,0 @@ -| Success | diff --git a/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.ql b/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.ql deleted file mode 100644 index 03549a97873..00000000000 --- a/javascript/ql/test/library-tests/RecursionPrevention/SourceNodeRange.ql +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Test that fails to compile if the domain of `SourceNode::Range` depends on `SourceNode` (recursively). - * - * This tests adds a negative dependency `SourceNode --!--> SourceNode::Range` - * so that the undesired edge `SourceNode::Range --> SourceNode` completes a negative cycle. - */ - -import javascript - -class BadSourceNodeRange extends DataFlow::SourceNode::Internal::RecursionGuard { - BadSourceNodeRange() { not this instanceof DataFlow::SourceNode::Range } -} - -select "Success" diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected index 96919f8717d..7df8bece899 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialBackTracking.expected @@ -38,6 +38,8 @@ | lib/snapdragon.js:15:26:15:27 | a* | Strings starting with 'a' and with many repetitions of 'a' can start matching anywhere after the start of the preceeding aa*$ | | lib/snapdragon.js:23:22:23:23 | a* | Strings starting with 'a' and with many repetitions of 'a' can start matching anywhere after the start of the preceeding aa*$ | | lib/subLib4/factory.js:8:3:8:4 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | +| lib/subLib5/feature.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b | +| lib/subLib5/main.js:2:3:2:4 | a* | Strings with many repetitions of 'a' can start matching anywhere after the start of the preceeding a*b | | lib/sublib/factory.js:13:14:13:15 | f* | Strings with many repetitions of 'f' can start matching anywhere after the start of the preceeding f*g | | polynomial-redos.js:7:24:7:26 | \\s+ | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding \\s+$ | | polynomial-redos.js:8:17:8:18 | * | Strings with many repetitions of ' ' can start matching anywhere after the start of the preceeding *, * | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected index dccfcc03ed3..d03213a37d0 100644 --- a/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected +++ b/javascript/ql/test/query-tests/Performance/ReDoS/PolynomialReDoS.expected @@ -51,6 +51,14 @@ nodes | lib/subLib4/factory.js:7:27:7:30 | name | | lib/subLib4/factory.js:8:13:8:16 | name | | lib/subLib4/factory.js:8:13:8:16 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | +| lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/main.js:1:28:1:31 | name | +| lib/subLib5/main.js:1:28:1:31 | name | +| lib/subLib5/main.js:2:13:2:16 | name | +| lib/subLib5/main.js:2:13:2:16 | name | | lib/sublib/factory.js:12:26:12:29 | name | | lib/sublib/factory.js:12:26:12:29 | name | | lib/sublib/factory.js:13:24:13:27 | name | @@ -256,6 +264,14 @@ edges | lib/subLib4/factory.js:7:27:7:30 | name | lib/subLib4/factory.js:8:13:8:16 | name | | lib/subLib4/factory.js:7:27:7:30 | name | lib/subLib4/factory.js:8:13:8:16 | name | | lib/subLib4/factory.js:7:27:7:30 | name | lib/subLib4/factory.js:8:13:8:16 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/feature.js:1:28:1:31 | name | lib/subLib5/feature.js:2:13:2:16 | name | +| lib/subLib5/main.js:1:28:1:31 | name | lib/subLib5/main.js:2:13:2:16 | name | +| lib/subLib5/main.js:1:28:1:31 | name | lib/subLib5/main.js:2:13:2:16 | name | +| lib/subLib5/main.js:1:28:1:31 | name | lib/subLib5/main.js:2:13:2:16 | name | +| lib/subLib5/main.js:1:28:1:31 | name | lib/subLib5/main.js:2:13:2:16 | name | | lib/sublib/factory.js:12:26:12:29 | name | lib/sublib/factory.js:13:24:13:27 | name | | lib/sublib/factory.js:12:26:12:29 | name | lib/sublib/factory.js:13:24:13:27 | name | | lib/sublib/factory.js:12:26:12:29 | name | lib/sublib/factory.js:13:24:13:27 | name | @@ -418,6 +434,8 @@ edges | lib/snapdragon.js:15:13:15:30 | this.match(/aa*$/) | lib/snapdragon.js:12:34:12:38 | input | lib/snapdragon.js:15:13:15:16 | this | This $@ that depends on $@ may run slow on strings starting with 'a' and with many repetitions of 'a'. | lib/snapdragon.js:15:26:15:27 | a* | regular expression | lib/snapdragon.js:12:34:12:38 | input | library input | | lib/snapdragon.js:23:5:23:26 | node.va ... /aa*$/) | lib/snapdragon.js:20:34:20:38 | input | lib/snapdragon.js:23:5:23:12 | node.val | This $@ that depends on $@ may run slow on strings starting with 'a' and with many repetitions of 'a'. | lib/snapdragon.js:23:22:23:23 | a* | regular expression | lib/snapdragon.js:20:34:20:38 | input | library input | | lib/subLib4/factory.js:8:2:8:17 | /f*g/.test(name) | lib/subLib4/factory.js:7:27:7:30 | name | lib/subLib4/factory.js:8:13:8:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/subLib4/factory.js:8:3:8:4 | f* | regular expression | lib/subLib4/factory.js:7:27:7:30 | name | library input | +| lib/subLib5/feature.js:2:2:2:17 | /a*b/.test(name) | lib/subLib5/feature.js:1:28:1:31 | name | lib/subLib5/feature.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/subLib5/feature.js:2:3:2:4 | a* | regular expression | lib/subLib5/feature.js:1:28:1:31 | name | library input | +| lib/subLib5/main.js:2:2:2:17 | /a*b/.test(name) | lib/subLib5/main.js:1:28:1:31 | name | lib/subLib5/main.js:2:13:2:16 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'a'. | lib/subLib5/main.js:2:3:2:4 | a* | regular expression | lib/subLib5/main.js:1:28:1:31 | name | library input | | lib/sublib/factory.js:13:13:13:28 | /f*g/.test(name) | lib/sublib/factory.js:12:26:12:29 | name | lib/sublib/factory.js:13:24:13:27 | name | This $@ that depends on $@ may run slow on strings with many repetitions of 'f'. | lib/sublib/factory.js:13:14:13:15 | f* | regular expression | lib/sublib/factory.js:12:26:12:29 | name | library input | | polynomial-redos.js:7:2:7:34 | tainted ... /g, '') | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:7:2:7:8 | tainted | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | polynomial-redos.js:7:24:7:26 | \\s+ | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | | polynomial-redos.js:8:2:8:23 | tainted ... *, */) | polynomial-redos.js:5:16:5:32 | req.query.tainted | polynomial-redos.js:8:2:8:8 | tainted | This $@ that depends on $@ may run slow on strings with many repetitions of ' '. | polynomial-redos.js:8:17:8:18 | * | regular expression | polynomial-redos.js:5:16:5:32 | req.query.tainted | a user-provided value | diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/feature.js b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/feature.js new file mode 100644 index 00000000000..4326227e86b --- /dev/null +++ b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/feature.js @@ -0,0 +1,3 @@ +module.exports = function (name) { + /a*b/.test(name); // NOT OK +}; diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/main.js b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/main.js new file mode 100644 index 00000000000..4326227e86b --- /dev/null +++ b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/main.js @@ -0,0 +1,3 @@ +module.exports = function (name) { + /a*b/.test(name); // NOT OK +}; diff --git a/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/package.json b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/package.json new file mode 100644 index 00000000000..f10fc5e5788 --- /dev/null +++ b/javascript/ql/test/query-tests/Performance/ReDoS/lib/subLib5/package.json @@ -0,0 +1,11 @@ +{ + "name": "my-sub-lib", + "version": "0.0.7", + "main": "./main.js", + "exports": { + ".": "./main.js", + "./feature": { + "default": "./feature.js" + } + } +} diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected index c604f4ab2d2..94f1fe314b0 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected @@ -50,6 +50,12 @@ nodes | main.js:79:34:79:36 | val | | main.js:81:35:81:37 | val | | main.js:81:35:81:37 | val | +| main.js:89:21:89:21 | x | +| main.js:90:23:90:23 | x | +| main.js:90:23:90:23 | x | +| main.js:93:43:93:43 | x | +| main.js:93:43:93:43 | x | +| main.js:94:31:94:31 | x | | typed.ts:1:39:1:39 | s | | typed.ts:1:39:1:39 | s | | typed.ts:2:29:2:29 | s | @@ -115,6 +121,11 @@ edges | main.js:79:34:79:36 | val | main.js:81:35:81:37 | val | | main.js:79:34:79:36 | val | main.js:81:35:81:37 | val | | main.js:79:34:79:36 | val | main.js:81:35:81:37 | val | +| main.js:89:21:89:21 | x | main.js:90:23:90:23 | x | +| main.js:89:21:89:21 | x | main.js:90:23:90:23 | x | +| main.js:93:43:93:43 | x | main.js:94:31:94:31 | x | +| main.js:93:43:93:43 | x | main.js:94:31:94:31 | x | +| main.js:94:31:94:31 | x | main.js:89:21:89:21 | x | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | @@ -141,5 +152,6 @@ edges | main.js:62:19:62:31 | settings.name | main.js:56:28:56:34 | options | main.js:62:19:62:31 | settings.name | $@ based on $@ might later cause $@. | main.js:62:19:62:31 | settings.name | HTML construction | main.js:56:28:56:34 | options | library input | main.js:62:11:62:40 | "" + ... "" | cross-site scripting | | main.js:67:63:67:69 | attrVal | main.js:66:35:66:41 | attrVal | main.js:67:63:67:69 | attrVal | $@ based on $@ might later cause $@. | main.js:67:63:67:69 | attrVal | HTML construction | main.js:66:35:66:41 | attrVal | library input | main.js:67:47:67:78 | "" | cross-site scripting | | main.js:81:35:81:37 | val | main.js:79:34:79:36 | val | main.js:81:35:81:37 | val | $@ based on $@ might later cause $@. | main.js:81:35:81:37 | val | HTML construction | main.js:79:34:79:36 | val | library input | main.js:81:24:81:49 | " ... /span>" | cross-site scripting | +| main.js:90:23:90:23 | x | main.js:93:43:93:43 | x | main.js:90:23:90:23 | x | $@ based on $@ might later cause $@. | main.js:90:23:90:23 | x | HTML construction | main.js:93:43:93:43 | x | library input | main.js:94:20:94:32 | createHTML(x) | cross-site scripting | | typed.ts:2:29:2:29 | s | typed.ts:1:39:1:39 | s | typed.ts:2:29:2:29 | s | $@ based on $@ might later cause $@. | typed.ts:2:29:2:29 | s | HTML construction | typed.ts:1:39:1:39 | s | library input | typed.ts:3:31:3:34 | html | cross-site scripting | | typed.ts:8:40:8:40 | s | typed.ts:6:43:6:43 | s | typed.ts:8:40:8:40 | s | $@ based on $@ might later cause $@. | typed.ts:8:40:8:40 | s | HTML construction | typed.ts:6:43:6:43 | s | library input | typed.ts:8:29:8:52 | " ... /span>" | cross-site scripting | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js index 1547ae86b24..2e9d344b1f3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/main.js @@ -85,3 +85,11 @@ module.exports.types = function (val) { $("#foo").html("" + val + ""); // OK } } + +function createHTML(x) { + return "" + x + ""; // NOT OK +} + +module.exports.usesCreateHTML = function (x) { + $("#foo").html(createHTML(x)); +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected new file mode 100644 index 00000000000..a79b3100a80 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.expected @@ -0,0 +1,3 @@ +| tst.js:8:9:8:19 | /\\/foo\\/.*/ | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/FOO/' will bypass the middleware. | tst.js:8:9:8:19 | /\\/foo\\/.*/ | pattern | tst.js:60:1:61:2 | app.get ... ware\\n}) | here | +| tst.js:14:5:14:28 | new Reg ... (.*)?') | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/FOO' will bypass the middleware. | tst.js:14:5:14:28 | new Reg ... (.*)?') | pattern | tst.js:60:1:61:2 | app.get ... ware\\n}) | here | +| tst.js:41:9:41:25 | /\\/foo\\/([0-9]+)/ | This route uses a case-sensitive path $@, but is guarding a case-insensitive path $@. A path such as '/FOO/' will bypass the middleware. | tst.js:41:9:41:25 | /\\/foo\\/([0-9]+)/ | pattern | tst.js:60:1:61:2 | app.get ... ware\\n}) | here | diff --git a/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.qlref b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.qlref new file mode 100644 index 00000000000..75705303770 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-178/CaseSensitiveMiddlewarePath.qlref @@ -0,0 +1 @@ +Security/CWE-178/CaseSensitiveMiddlewarePath.ql diff --git a/javascript/ql/test/query-tests/Security/CWE-178/charclass.js b/javascript/ql/test/query-tests/Security/CWE-178/charclass.js new file mode 100644 index 00000000000..f10e0a2d7ab --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-178/charclass.js @@ -0,0 +1,9 @@ +const express = require('express'); +const app = express(); + +app.get(/\/[a-zA-Z]+/, (req, res, next) => { // OK - regexp term is case insensitive + next(); +}); + +app.get('/foo', (req, res) => { +}); diff --git a/javascript/ql/test/query-tests/Security/CWE-178/tst.js b/javascript/ql/test/query-tests/Security/CWE-178/tst.js new file mode 100644 index 00000000000..1acb57b16ea --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-178/tst.js @@ -0,0 +1,61 @@ +const express = require('express'); +const app = express(); +const unknown = require('~something/blah'); + +app.all(/\/.*/, unknown()); // OK - does not contain letters +app.all(/\/.*/i, unknown()); // OK + +app.all(/\/foo\/.*/, unknown()); // NOT OK +app.all(/\/foo\/.*/i, unknown()); // OK - case insensitive + +app.use(/\/x\/#\d{6}/, express.static('images/')); // OK - not a middleware + +app.get( + new RegExp('^/foo(.*)?'), // NOT OK - case sensitive + unknown(), + function(req, res, next) { + if (req.params.blah) { + next(); + } + } +); + +app.get( + new RegExp('^/foo(.*)?', 'i'), // OK - case insensitive + unknown(), + function(req, res, next) { + if (req.params.blah) { + next(); + } + } +); + +app.get( + new RegExp('^/foo(.*)?'), // OK - not a middleware + unknown(), + function(req,res) { + res.send('Hello World!'); + } +); + +app.use(/\/foo\/([0-9]+)/, (req, res, next) => { // NOT OK - case sensitive + unknown(req); + next(); +}); + +app.use(/\/foo\/([0-9]+)/i, (req, res, next) => { // OK - case insensitive + unknown(req); + next(); +}); + + +app.use(/\/foo\/([0-9]+)/, (req, res) => { // OK - not middleware + unknown(req, res); +}); + +app.use(/\/foo\/([0-9]+)/i, (req, res) => { // OK - not middleware (also case insensitive) + unknown(req, res); +}); + +app.get('/foo/:param', (req, res) => { // OK - not a middleware +}); diff --git a/python/ql/lib/CHANGELOG.md b/python/ql/lib/CHANGELOG.md index 83861bcf61d..b57f612b336 100644 --- a/python/ql/lib/CHANGELOG.md +++ b/python/ql/lib/CHANGELOG.md @@ -1,3 +1,20 @@ +## 0.5.2 + +## 0.5.1 + +### Deprecated APIs + +- The documentation of API graphs (the `API` module) has been expanded, and some of the members predicates of `API::Node` + have been renamed as follows: + - `getAnImmediateUse` -> `asSource` + - `getARhs` -> `asSink` + - `getAUse` -> `getAValueReachableFromSource` + - `getAValueReachingRhs` -> `getAValueReachingSink` + +### Minor Analysis Improvements + +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). + ## 0.5.0 ### Deprecated APIs diff --git a/python/ql/src/analysis/LocalDefinitions.ql b/python/ql/lib/analysis/LocalDefinitions.ql similarity index 100% rename from python/ql/src/analysis/LocalDefinitions.ql rename to python/ql/lib/analysis/LocalDefinitions.ql diff --git a/python/ql/src/analysis/LocalReferences.ql b/python/ql/lib/analysis/LocalReferences.ql similarity index 100% rename from python/ql/src/analysis/LocalReferences.ql rename to python/ql/lib/analysis/LocalReferences.ql diff --git a/python/ql/lib/change-notes/2022-06-15-class-decorator-api-subclass.md b/python/ql/lib/change-notes/2022-06-15-class-decorator-api-subclass.md new file mode 100644 index 00000000000..04beefb14b6 --- /dev/null +++ b/python/ql/lib/change-notes/2022-06-15-class-decorator-api-subclass.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Change `.getASubclass()` on `API::Node` so it allows to follow subclasses even if the class has a class decorator. diff --git a/python/ql/lib/change-notes/2022-06-22-sensitive-common-words.md b/python/ql/lib/change-notes/2022-06-22-sensitive-common-words.md deleted file mode 100644 index 9102d58abdb..00000000000 --- a/python/ql/lib/change-notes/2022-06-22-sensitive-common-words.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). diff --git a/python/ql/lib/change-notes/2022-06-28-api-graph-api.md b/python/ql/lib/change-notes/released/0.5.1.md similarity index 58% rename from python/ql/lib/change-notes/2022-06-28-api-graph-api.md rename to python/ql/lib/change-notes/released/0.5.1.md index ef6ff3d4f76..1b560c7a35a 100644 --- a/python/ql/lib/change-notes/2022-06-28-api-graph-api.md +++ b/python/ql/lib/change-notes/released/0.5.1.md @@ -1,6 +1,6 @@ ---- -category: deprecated ---- +## 0.5.1 + +### Deprecated APIs - The documentation of API graphs (the `API` module) has been expanded, and some of the members predicates of `API::Node` have been renamed as follows: @@ -8,3 +8,7 @@ category: deprecated - `getARhs` -> `asSink` - `getAUse` -> `getAValueReachableFromSource` - `getAValueReachingRhs` -> `getAValueReachingSink` + +### Minor Analysis Improvements + +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). diff --git a/python/ql/lib/change-notes/released/0.5.2.md b/python/ql/lib/change-notes/released/0.5.2.md new file mode 100644 index 00000000000..33ae68a2827 --- /dev/null +++ b/python/ql/lib/change-notes/released/0.5.2.md @@ -0,0 +1 @@ +## 0.5.2 diff --git a/python/ql/lib/codeql-pack.release.yml b/python/ql/lib/codeql-pack.release.yml index 30e271c5361..2d9d3f587f8 100644 --- a/python/ql/lib/codeql-pack.release.yml +++ b/python/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.5.0 +lastReleaseVersion: 0.5.2 diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index 65145e40d74..20d79f44e49 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-all -version: 0.5.1-dev +version: 0.5.3-dev groups: python dbscheme: semmlecode.python.dbscheme extractor: python diff --git a/python/ql/lib/semmle/python/ApiGraphs.qll b/python/ql/lib/semmle/python/ApiGraphs.qll index e5f506d500c..41715721129 100644 --- a/python/ql/lib/semmle/python/ApiGraphs.qll +++ b/python/ql/lib/semmle/python/ApiGraphs.qll @@ -719,9 +719,16 @@ module API { or // Subclassing a node lbl = Label::subclass() and - exists(DataFlow::Node superclass | pred.flowsTo(superclass) | - ref.(DataFlow::ExprNode).getNode().getNode().(PY::ClassExpr).getABase() = - superclass.(DataFlow::ExprNode).getNode().getNode() + exists(PY::ClassExpr clsExpr, DataFlow::Node superclass | pred.flowsTo(superclass) | + clsExpr.getABase() = superclass.asExpr() and + // Potentially a class decorator could do anything, but we assume they are + // "benign" and let subclasses edges flow through anyway. + // see example in https://github.com/django/django/blob/c2250cfb80e27cdf8d098428824da2800a18cadf/tests/auth_tests/test_views.py#L40-L46 + ( + ref.(DataFlow::ExprNode).getNode().getNode() = clsExpr + or + ref.(DataFlow::ExprNode).getNode().getNode() = clsExpr.getADecoratorCall() + ) ) or // awaiting diff --git a/python/ql/lib/semmle/python/Frameworks.qll b/python/ql/lib/semmle/python/Frameworks.qll index daa67ee4231..6cf4044157b 100644 --- a/python/ql/lib/semmle/python/Frameworks.qll +++ b/python/ql/lib/semmle/python/Frameworks.qll @@ -2,7 +2,7 @@ * Helper file that imports all framework modeling. */ -// If you add modeling of a new framework/library, remember to add it it to the docs in +// If you add modeling of a new framework/library, remember to add it to the docs in // `docs/codeql/support/reusables/frameworks.rst` private import semmle.python.frameworks.Aioch private import semmle.python.frameworks.Aiohttp diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll index a076f7a2e45..340bfe280b7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index a076f7a2e45..340bfe280b7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index a076f7a2e45..340bfe280b7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index a076f7a2e45..340bfe280b7 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll index e67fafef71e..ffbcdac5d16 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowPublic.qll @@ -554,16 +554,55 @@ class StarPatternElementNode extends Node, TStarPatternElementNode { override Location getLocation() { result = consumer.getLocation() } } +/** + * Gets a node that controls whether other nodes are evaluated. + * + * In the base case, this is the last node of `conditionBlock`, and `flipped` is `false`. + * This definition accounts for (short circuting) `and`- and `or`-expressions, as the structure + * of basic blocks will reflect their semantics. + * + * However, in the program + * ```python + * if not is_safe(path): + * return + * ``` + * the last node in the `ConditionBlock` is `not is_safe(path)`. + * + * We would like to consider also `is_safe(path)` a guard node, albeit with `flipped` being `true`. + * Thus we recurse through `not`-expressions. + */ +ControlFlowNode guardNode(ConditionBlock conditionBlock, boolean flipped) { + // Base case: the last node truly does determine which successor is chosen + result = conditionBlock.getLastNode() and + flipped = false + or + // Recursive case: if a guard node is a `not`-expression, + // the operand is also a guard node, but with inverted polarity. + exists(UnaryExprNode notNode | + result = notNode.getOperand() and + notNode.getNode().getOp() instanceof Not + | + notNode = guardNode(conditionBlock, flipped.booleanNot()) + ) +} + /** * A node that controls whether other nodes are evaluated. + * + * The field `flipped` allows us to match `GuardNode`s underneath + * `not`-expressions and still choose the appropriate branch. */ class GuardNode extends ControlFlowNode { ConditionBlock conditionBlock; + boolean flipped; - GuardNode() { this = conditionBlock.getLastNode() } + GuardNode() { this = guardNode(conditionBlock, flipped) } /** Holds if this guard controls block `b` upon evaluating to `branch`. */ - predicate controlsBlock(BasicBlock b, boolean branch) { conditionBlock.controls(b, branch) } + predicate controlsBlock(BasicBlock b, boolean branch) { + branch in [true, false] and + conditionBlock.controls(b, branch.booleanXor(flipped)) + } } /** diff --git a/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll b/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll index 22832b46c6c..d0293522404 100755 --- a/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll +++ b/python/ql/lib/semmle/python/dataflow/old/TaintTracking.qll @@ -333,7 +333,7 @@ abstract class Sanitizer extends string { /** Holds if `taint` cannot flow through `node`. */ predicate sanitizingNode(TaintKind taint, ControlFlowNode node) { none() } - /** Holds if `call` removes removes the `taint` */ + /** Holds if `call` removes the `taint` */ predicate sanitizingCall(TaintKind taint, FunctionObject callee) { none() } /** Holds if `test` shows value to be untainted with `taint` */ diff --git a/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll b/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll index 47521ac0b31..04b0a9e4afd 100644 --- a/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll +++ b/python/ql/lib/semmle/python/frameworks/internal/SubclassFinder.qll @@ -74,9 +74,7 @@ private module NotExposed { } /** DEPRECATED: Alias for fullyQualifiedToApiGraphPath */ - deprecated string fullyQualifiedToAPIGraphPath(string fullyQaulified) { - result = fullyQualifiedToApiGraphPath(fullyQaulified) - } + deprecated predicate fullyQualifiedToAPIGraphPath = fullyQualifiedToApiGraphPath/1; bindingset[this] abstract class FindSubclassesSpec extends string { diff --git a/python/ql/lib/semmle/python/pointsto/PointsToContext.qll b/python/ql/lib/semmle/python/pointsto/PointsToContext.qll index 0e5f52b01b8..ad86a49ba6f 100644 --- a/python/ql/lib/semmle/python/pointsto/PointsToContext.qll +++ b/python/ql/lib/semmle/python/pointsto/PointsToContext.qll @@ -23,13 +23,8 @@ private int max_context_cost() { } private int syntactic_call_count(Scope s) { - exists(Function f | f = s and f.getName() != "__init__" | - result = - count(CallNode call | - call.getFunction().(NameNode).getId() = f.getName() - or - call.getFunction().(AttrNode).getName() = f.getName() - ) + exists(Function f, string name | f = s and name = f.getName() and name != "__init__" | + result = count(function_call(name)) + count(method_call(name)) ) or s.getName() = "__init__" and result = 1 @@ -37,6 +32,12 @@ private int syntactic_call_count(Scope s) { not s instanceof Function and result = 0 } +pragma[nomagic] +private CallNode function_call(string name) { result.getFunction().(NameNode).getId() = name } + +pragma[nomagic] +private CallNode method_call(string name) { result.getFunction().(AttrNode).getName() = name } + private int incoming_call_cost(Scope s) { /* * Syntactic call count will often be a considerable overestimate diff --git a/python/ql/src/CHANGELOG.md b/python/ql/src/CHANGELOG.md index 3be12c71c5f..8fdacb47f64 100644 --- a/python/ql/src/CHANGELOG.md +++ b/python/ql/src/CHANGELOG.md @@ -1,3 +1,15 @@ +## 0.4.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package. + +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package. + ## 0.2.0 ### Major Analysis Improvements diff --git a/python/ql/src/Security/CWE-020/HostnameRegexpShared.qll b/python/ql/src/Security/CWE-020/HostnameRegexpShared.qll index 2192951f76d..5e9bc406512 100644 --- a/python/ql/src/Security/CWE-020/HostnameRegexpShared.qll +++ b/python/ql/src/Security/CWE-020/HostnameRegexpShared.qll @@ -53,7 +53,7 @@ predicate matchesBeginningOfString(RegExpTerm term) { } /** - * Holds if the given sequence contains top-level domain preceded by a dot, such as `.com`, + * Holds if the given sequence `seq` contains top-level domain preceded by a dot, such as `.com`, * excluding cases where this is at the very beginning of the regexp. * * `i` is bound to the index of the last child in the top-level domain part. diff --git a/python/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/python/ql/src/change-notes/released/0.3.0.md similarity index 77% rename from python/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename to python/ql/src/change-notes/released/0.3.0.md index 2e8562e66f8..7b54313449c 100644 --- a/python/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ b/python/ql/src/change-notes/released/0.3.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 0.3.0 + +### Breaking Changes + * Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package. diff --git a/python/ql/src/change-notes/released/0.4.0.md b/python/ql/src/change-notes/released/0.4.0.md new file mode 100644 index 00000000000..c6658b7780f --- /dev/null +++ b/python/ql/src/change-notes/released/0.4.0.md @@ -0,0 +1,5 @@ +## 0.4.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/python-all` package. diff --git a/python/ql/src/codeql-pack.release.yml b/python/ql/src/codeql-pack.release.yml index 5274e27ed52..458bfbeccff 100644 --- a/python/ql/src/codeql-pack.release.yml +++ b/python/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.4.0 diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py new file mode 100644 index 00000000000..96aeb436a9b --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.py @@ -0,0 +1,7 @@ +blob_client = blob_service_client.get_blob_client(container=container_name, blob=blob_name) +blob_client.require_encryption = True +blob_client.key_encryption_key = kek +# GOOD: Must use `encryption_version` set to `2.0` +blob_client.encryption_version = '2.0' # Use Version 2.0! +with open("decryptedcontentfile.txt", "rb") as stream: + blob_client.upload_blob(stream, overwrite=OVERWRITE_EXISTING) \ No newline at end of file diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp new file mode 100644 index 00000000000..a2f9a2213c3 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.qhelp @@ -0,0 +1,29 @@ + + + + + +

    Azure Storage .NET, Java, and Python SDKs support encryption on the client with a customer-managed key that is maintained in Azure Key Vault or another key store.

    +

    Current release versions of the Azure Storage SDKs use cipher block chaining (CBC mode) for client-side encryption (referred to as v1).

    + +
    + + +

    Consider switching to v2 client-side encryption.

    + +
    + + + + + + +
  • + Azure Storage Client Encryption Blog. +
  • +
  • + CVE-2022-30187 +
  • + +
    +
    diff --git a/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql new file mode 100644 index 00000000000..c9687b17821 --- /dev/null +++ b/python/ql/src/experimental/Security/CWE-327/Azure/UnsafeUsageOfClientSideEncryptionVersion.ql @@ -0,0 +1,90 @@ +/** + * @name Unsafe usage of v1 version of Azure Storage client-side encryption. + * @description Using version v1 of Azure Storage client-side encryption is insecure, and may enable an attacker to decrypt encrypted data + * @kind problem + * @tags security + * cryptography + * external/cwe/cwe-327 + * @id py/azure-storage/unsafe-client-side-encryption-in-use + * @problem.severity error + * @precision medium + */ + +import python +import semmle.python.ApiGraphs + +predicate isUnsafeClientSideAzureStorageEncryptionViaAttributes(Call call, AttrNode node) { + exists( + API::Node n, API::Node n2, Attribute a, AssignStmt astmt, API::Node uploadBlob, + ControlFlowNode ctrlFlowNode, string s + | + s in ["key_encryption_key", "key_resolver_function"] and + n = + API::moduleImport("azure") + .getMember("storage") + .getMember("blob") + .getMember("BlobClient") + .getReturn() + .getMember(s) and + n2 = + API::moduleImport("azure") + .getMember("storage") + .getMember("blob") + .getMember("BlobClient") + .getReturn() + .getMember("upload_blob") and + n.getAValueReachableFromSource().asExpr() = a and + astmt.getATarget() = a and + a.getAFlowNode() = node and + uploadBlob = + API::moduleImport("azure") + .getMember("storage") + .getMember("blob") + .getMember("BlobClient") + .getReturn() + .getMember("upload_blob") and + uploadBlob.getACall().asExpr() = call and + ctrlFlowNode = call.getAFlowNode() and + node.strictlyReaches(ctrlFlowNode) and + node != ctrlFlowNode and + not exists( + AssignStmt astmt2, Attribute a2, AttrNode encryptionVersionSet, StrConst uc, + API::Node encryptionVersion + | + uc = astmt2.getValue() and + uc.getText() in ["'2.0'", "2.0"] and + encryptionVersion = + API::moduleImport("azure") + .getMember("storage") + .getMember("blob") + .getMember("BlobClient") + .getReturn() + .getMember("encryption_version") and + encryptionVersion.getAValueReachableFromSource().asExpr() = a2 and + astmt2.getATarget() = a2 and + a2.getAFlowNode() = encryptionVersionSet and + encryptionVersionSet.strictlyReaches(ctrlFlowNode) + ) + ) +} + +predicate isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(Call call, ControlFlowNode node) { + exists(API::Node c, string s, Keyword k | k.getAFlowNode() = node | + c.getACall().asExpr() = call and + c = API::moduleImport("azure").getMember("storage").getMember("blob").getMember(s) and + s in ["ContainerClient", "BlobClient", "BlobServiceClient"] and + k.getArg() = "key_encryption_key" and + k = call.getANamedArg() and + not k.getValue() instanceof None and + not exists(Keyword k2 | k2 = call.getANamedArg() | + k2.getArg() = "encryption_version" and + k2.getValue().(StrConst).getText() in ["'2.0'", "2.0"] + ) + ) +} + +from Call call, ControlFlowNode node +where + isUnsafeClientSideAzureStorageEncryptionViaAttributes(call, node) or + isUnsafeClientSideAzureStorageEncryptionViaObjectCreation(call, node) +select node, "Unsafe usage of v1 version of Azure Storage client-side encryption." diff --git a/python/ql/src/qlpack.yml b/python/ql/src/qlpack.yml index af722116e41..75227225c64 100644 --- a/python/ql/src/qlpack.yml +++ b/python/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/python-queries -version: 0.2.1-dev +version: 0.4.1-dev groups: - python - queries diff --git a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/test_string_const_compare.py b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/test_string_const_compare.py index 2fc809cf18f..f1b01f4f84a 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/test_string_const_compare.py +++ b/python/ql/test/experimental/dataflow/tainttracking/commonSanitizer/test_string_const_compare.py @@ -50,7 +50,7 @@ def test_non_eq2(): if not ts == "safe": ensure_tainted(ts) # $ tainted else: - ensure_not_tainted(ts) # $ SPURIOUS: tainted + ensure_not_tainted(ts) def test_in_list(): @@ -157,7 +157,7 @@ def test_not_in2(): if not ts in ["safe", "also_safe"]: ensure_tainted(ts) # $ tainted else: - ensure_not_tainted(ts) # $ SPURIOUS: tainted + ensure_not_tainted(ts) def is_safe(x): diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected index c8c41375538..fdad063534b 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.expected @@ -6,12 +6,20 @@ isSanitizer | TestTaintTrackingConfiguration | test.py:34:39:34:39 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test.py:52:28:52:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test.py:66:10:66:29 | ControlFlowNode for emulated_escaping() | -| TestTaintTrackingConfiguration | test_logical.py:30:28:30:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:45:28:45:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:50:28:50:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:89:28:89:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:100:28:100:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:145:28:145:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:33:28:33:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:40:28:40:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:48:28:48:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:53:28:53:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:92:28:92:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:103:28:103:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:111:28:111:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:130:28:130:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:137:28:137:28 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_logical.py:148:28:148:28 | ControlFlowNode for s | -| TestTaintTrackingConfiguration | test_logical.py:155:28:155:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:151:28:151:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:158:28:158:28 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:167:24:167:24 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:176:24:176:24 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:185:24:185:24 | ControlFlowNode for s | +| TestTaintTrackingConfiguration | test_logical.py:193:24:193:24 | ControlFlowNode for s | | TestTaintTrackingConfiguration | test_reference.py:31:28:31:28 | ControlFlowNode for s | diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql index c68bd2d274d..984cf74d036 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/InlineTaintTest.ql @@ -6,6 +6,12 @@ predicate isSafeCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branc branch = true } +predicate isUnsafeCheck(DataFlow::GuardNode g, ControlFlowNode node, boolean branch) { + g.(CallNode).getNode().getFunc().(Name).getId() in ["is_unsafe", "emulated_is_unsafe"] and + node = g.(CallNode).getAnArg() and + branch = false +} + class CustomSanitizerOverrides extends TestTaintTrackingConfiguration { override predicate isSanitizer(DataFlow::Node node) { exists(Call call | @@ -16,6 +22,8 @@ class CustomSanitizerOverrides extends TestTaintTrackingConfiguration { node.asExpr().(Call).getFunc().(Name).getId() = "emulated_escaping" or node = DataFlow::BarrierGuard::getABarrierNode() + or + node = DataFlow::BarrierGuard::getABarrierNode() } } diff --git a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py index a879c3c332c..26e69b8fc05 100644 --- a/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py +++ b/python/ql/test/experimental/dataflow/tainttracking/customSanitizer/test_logical.py @@ -22,6 +22,9 @@ def random_choice(): def is_safe(arg): return arg == "safe" +def is_unsafe(arg): + return arg == TAINTED_STRING + def test_basic(): s = TAINTED_STRING @@ -34,7 +37,7 @@ def test_basic(): if not is_safe(s): ensure_tainted(s) # $ tainted else: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) def test_if_in_depth(): @@ -105,7 +108,7 @@ def test_and(): ensure_tainted(s) # $ tainted else: # cannot be tainted - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) def test_tricky(): @@ -124,14 +127,14 @@ def test_nesting_not(): s = TAINTED_STRING if not(not(is_safe(s))): - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) else: ensure_tainted(s) # $ tainted if not(not(not(is_safe(s)))): ensure_tainted(s) # $ tainted else: - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) # Adding `and True` makes the sanitizer trigger when it would otherwise not. See output in @@ -161,7 +164,16 @@ def test_with_return(): if not is_safe(s): return - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) + + +def test_with_return_neg(): + s = TAINTED_STRING + + if is_unsafe(s): + return + + ensure_not_tainted(s) def test_with_exception(): @@ -170,7 +182,15 @@ def test_with_exception(): if not is_safe(s): raise Exception("unsafe") - ensure_not_tainted(s) # $ SPURIOUS: tainted + ensure_not_tainted(s) + +def test_with_exception_neg(): + s = TAINTED_STRING + + if is_unsafe(s): + raise Exception("unsafe") + + ensure_not_tainted(s) # Make tests runable @@ -182,7 +202,12 @@ test_tricky() test_nesting_not() test_nesting_not_with_and_true() test_with_return() +test_with_return_neg() try: test_with_exception() except: pass +try: + test_with_exception_neg() +except: + pass diff --git a/python/ql/test/experimental/dataflow/typetracking/test.py b/python/ql/test/experimental/dataflow/typetracking/test.py index 4392e3bbf46..819d85c1d8a 100644 --- a/python/ql/test/experimental/dataflow/typetracking/test.py +++ b/python/ql/test/experimental/dataflow/typetracking/test.py @@ -65,6 +65,9 @@ def to_inner_scope(): also_x = foo() # $ MISSING: tracked print(also_x) # $ MISSING: tracked +# ------------------------------------------------------------------------------ +# Function decorator +# ------------------------------------------------------------------------------ def my_decorator(func): # This part doesn't make any sense in a normal decorator, but just shows how we @@ -135,7 +138,7 @@ class Bar(Foo): def track_self(self): # $ tracked_self self.meth1() # $ tracked_self super().meth2() - super(Bar, self).foo3() # $ tracked_self + super(Bar, self).meth3() # $ tracked_self # ------------------------------------------------------------------------------ # Tracking of attribute lookup after "long" import chain diff --git a/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py b/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py index 63234fa11fc..ef15ece04f6 100644 --- a/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py +++ b/python/ql/test/library-tests/ApiGraphs/py3/deftest2.py @@ -16,4 +16,19 @@ def internal(): def my_internal_method(self): #$ def=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getMember("my_internal_method") pass - int_instance = IntMyView() #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getReturn() \ No newline at end of file + int_instance = IntMyView() #$ use=moduleImport("pflask").getMember("views").getMember("View").getASubclass().getReturn() + +# ------------------------------------------------------------------------------ +# Class decorator +# ------------------------------------------------------------------------------ + +def my_class_decorator(cls): + print("dummy decorator") + return cls + +@my_class_decorator +class MyViewWithDecorator(View): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass() + pass + +class SubclassFromDecorated(MyViewWithDecorator): #$ use=moduleImport("flask").getMember("views").getMember("View").getASubclass().getASubclass() + pass diff --git a/python/ql/test/query-tests/Security/CWE-022-TarSlip/TarSlip.expected b/python/ql/test/query-tests/Security/CWE-022-TarSlip/TarSlip.expected index 2ddfe7143d0..3cd40605b96 100644 --- a/python/ql/test/query-tests/Security/CWE-022-TarSlip/TarSlip.expected +++ b/python/ql/test/query-tests/Security/CWE-022-TarSlip/TarSlip.expected @@ -7,8 +7,6 @@ edges | tarslip.py:40:7:40:39 | ControlFlowNode for Attribute() | tarslip.py:41:24:41:26 | ControlFlowNode for tar | | tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | tarslip.py:57:5:57:9 | GSSA Variable entry | | tarslip.py:57:5:57:9 | GSSA Variable entry | tarslip.py:59:21:59:25 | ControlFlowNode for entry | -| tarslip.py:79:7:79:39 | ControlFlowNode for Attribute() | tarslip.py:80:5:80:9 | GSSA Variable entry | -| tarslip.py:80:5:80:9 | GSSA Variable entry | tarslip.py:82:21:82:25 | ControlFlowNode for entry | nodes | tarslip.py:12:7:12:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | tarslip.py:13:1:13:3 | ControlFlowNode for tar | semmle.label | ControlFlowNode for tar | @@ -23,9 +21,6 @@ nodes | tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | tarslip.py:57:5:57:9 | GSSA Variable entry | semmle.label | GSSA Variable entry | | tarslip.py:59:21:59:25 | ControlFlowNode for entry | semmle.label | ControlFlowNode for entry | -| tarslip.py:79:7:79:39 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | -| tarslip.py:80:5:80:9 | GSSA Variable entry | semmle.label | GSSA Variable entry | -| tarslip.py:82:21:82:25 | ControlFlowNode for entry | semmle.label | ControlFlowNode for entry | subpaths #select | tarslip.py:13:1:13:3 | ControlFlowNode for tar | tarslip.py:12:7:12:39 | ControlFlowNode for Attribute() | tarslip.py:13:1:13:3 | ControlFlowNode for tar | Extraction of tarfile from $@ | tarslip.py:12:7:12:39 | ControlFlowNode for Attribute() | a potentially untrusted source | @@ -33,4 +28,3 @@ subpaths | tarslip.py:37:17:37:21 | ControlFlowNode for entry | tarslip.py:33:7:33:39 | ControlFlowNode for Attribute() | tarslip.py:37:17:37:21 | ControlFlowNode for entry | Extraction of tarfile from $@ | tarslip.py:33:7:33:39 | ControlFlowNode for Attribute() | a potentially untrusted source | | tarslip.py:41:24:41:26 | ControlFlowNode for tar | tarslip.py:40:7:40:39 | ControlFlowNode for Attribute() | tarslip.py:41:24:41:26 | ControlFlowNode for tar | Extraction of tarfile from $@ | tarslip.py:40:7:40:39 | ControlFlowNode for Attribute() | a potentially untrusted source | | tarslip.py:59:21:59:25 | ControlFlowNode for entry | tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | tarslip.py:59:21:59:25 | ControlFlowNode for entry | Extraction of tarfile from $@ | tarslip.py:56:7:56:39 | ControlFlowNode for Attribute() | a potentially untrusted source | -| tarslip.py:82:21:82:25 | ControlFlowNode for entry | tarslip.py:79:7:79:39 | ControlFlowNode for Attribute() | tarslip.py:82:21:82:25 | ControlFlowNode for entry | Extraction of tarfile from $@ | tarslip.py:79:7:79:39 | ControlFlowNode for Attribute() | a potentially untrusted source | diff --git a/ql/README.md b/ql/README.md index 4bb5e30ba84..29b5aaef63c 100644 --- a/ql/README.md +++ b/ql/README.md @@ -21,14 +21,14 @@ cargo build --release The generated `ql/src/ql.dbscheme` and `ql/src/codeql_ql/ast/internal/TreeSitter.qll` files are included in the repository, but they can be re-generated as follows: ```bash -./create-extractor-pack.sh +./scripts/create-extractor-pack.sh ``` ## Building a CodeQL database for a QL program First, get an extractor pack: -Run `./create-extractor-pack.sh` (Linux/Mac) or `.\create-extractor-pack.ps1` (Windows PowerShell) and the pack will be created in the `extractor-pack` directory. +Run `./scripts/create-extractor-pack.sh` (Linux/Mac) or `.\scripts\create-extractor-pack.ps1` (Windows PowerShell) and the pack will be created in the `extractor-pack` directory. Then run diff --git a/ql/autobuilder/src/main.rs b/ql/autobuilder/src/main.rs index 45977b3792d..df47cc33184 100644 --- a/ql/autobuilder/src/main.rs +++ b/ql/autobuilder/src/main.rs @@ -15,29 +15,25 @@ fn main() -> std::io::Result<()> { let mut cmd = Command::new(codeql); cmd.arg("database") .arg("index-files") + .arg("--include-extension=.ql") + .arg("--include-extension=.qll") + .arg("--include-extension=.dbscheme") + .arg("--include=**/qlpack.yml") .arg("--size-limit=5m") .arg("--language=ql") .arg("--working-dir=.") .arg(db); - let mut has_include_dir = false; // TODO: This is a horrible hack, wait for the post-merge discussion in https://github.com/github/codeql/pull/7444 to be resolved for line in env::var("LGTM_INDEX_FILTERS") .unwrap_or_default() .split('\n') { if let Some(stripped) = line.strip_prefix("include:") { - cmd.arg("--include").arg(stripped); - has_include_dir = true; + cmd.arg("--also-match=".to_owned() + stripped); } else if let Some(stripped) = line.strip_prefix("exclude:") { - cmd.arg("--exclude").arg(stripped); + cmd.arg("--exclude=".to_owned() + stripped); } } - if !has_include_dir { - cmd.arg("--include-extension=.ql") - .arg("--include-extension=.qll") - .arg("--include-extension=.dbscheme") - .arg("--include=**/qlpack.yml"); - } let exit = &cmd.spawn()?.wait()?; std::process::exit(exit.code().unwrap_or(1)) } diff --git a/ql/extractor/src/extractor.rs b/ql/extractor/src/extractor.rs index 8cdaff1b738..db280634ae5 100644 --- a/ql/extractor/src/extractor.rs +++ b/ql/extractor/src/extractor.rs @@ -1,161 +1,112 @@ +use crate::trap; use node_types::{EntryKind, Field, NodeTypeMap, Storage, TypeName}; -use std::borrow::Cow; use std::collections::BTreeMap as Map; use std::collections::BTreeSet as Set; use std::fmt; -use std::io::Write; use std::path::Path; use tracing::{error, info, span, Level}; use tree_sitter::{Language, Node, Parser, Range, Tree}; -pub struct TrapWriter { - /// The accumulated trap entries - trap_output: Vec, - /// A counter for generating fresh labels - counter: u32, - /// cache of global keys - global_keys: std::collections::HashMap, +pub fn populate_file(writer: &mut trap::Writer, absolute_path: &Path) -> trap::Label { + let (file_label, fresh) = + writer.global_id(&trap::full_id_for_file(&normalize_path(absolute_path))); + if fresh { + writer.add_tuple( + "files", + vec![ + trap::Arg::Label(file_label), + trap::Arg::String(normalize_path(absolute_path)), + ], + ); + populate_parent_folders(writer, file_label, absolute_path.parent()); + } + file_label } -pub fn new_trap_writer() -> TrapWriter { - TrapWriter { - counter: 0, - trap_output: Vec::new(), - global_keys: std::collections::HashMap::new(), +fn populate_empty_file(writer: &mut trap::Writer) -> trap::Label { + let (file_label, fresh) = writer.global_id("empty;sourcefile"); + if fresh { + writer.add_tuple( + "files", + vec![ + trap::Arg::Label(file_label), + trap::Arg::String("".to_string()), + ], + ); } + file_label } -impl TrapWriter { - /// Gets a label that will hold the unique ID of the passed string at import time. - /// This can be used for incrementally importable TRAP files -- use globally unique - /// strings to compute a unique ID for table tuples. - /// - /// Note: You probably want to make sure that the key strings that you use are disjoint - /// for disjoint column types; the standard way of doing this is to prefix (or append) - /// the column type name to the ID. Thus, you might identify methods in Java by the - /// full ID "methods_com.method.package.DeclaringClass.method(argumentList)". +pub fn populate_empty_location(writer: &mut trap::Writer) { + let file_label = populate_empty_file(writer); + location(writer, file_label, 0, 0, 0, 0); +} - fn fresh_id(&mut self) -> Label { - let label = Label(self.counter); - self.counter += 1; - self.trap_output.push(TrapEntry::FreshId(label)); - label - } - - fn global_id(&mut self, key: &str) -> (Label, bool) { - if let Some(label) = self.global_keys.get(key) { - return (*label, false); - } - let label = Label(self.counter); - self.counter += 1; - self.global_keys.insert(key.to_owned(), label); - self.trap_output - .push(TrapEntry::MapLabelToKey(label, key.to_owned())); - (label, true) - } - - fn add_tuple(&mut self, table_name: &str, args: Vec) { - self.trap_output - .push(TrapEntry::GenericTuple(table_name.to_owned(), args)) - } - - fn populate_file(&mut self, absolute_path: &Path) -> Label { - let (file_label, fresh) = self.global_id(&full_id_for_file(absolute_path)); - if fresh { - self.add_tuple( - "files", - vec![ - Arg::Label(file_label), - Arg::String(normalize_path(absolute_path)), - ], - ); - self.populate_parent_folders(file_label, absolute_path.parent()); - } - file_label - } - - fn populate_empty_file(&mut self) -> Label { - let (file_label, fresh) = self.global_id("empty;sourcefile"); - if fresh { - self.add_tuple( - "files", - vec![Arg::Label(file_label), Arg::String("".to_string())], - ); - } - file_label - } - - pub fn populate_empty_location(&mut self) { - let file_label = self.populate_empty_file(); - self.location(file_label, 0, 0, 0, 0); - } - - fn populate_parent_folders(&mut self, child_label: Label, path: Option<&Path>) { - let mut path = path; - let mut child_label = child_label; - loop { - match path { - None => break, - Some(folder) => { - let (folder_label, fresh) = self.global_id(&full_id_for_folder(folder)); - self.add_tuple( - "containerparent", - vec![Arg::Label(folder_label), Arg::Label(child_label)], +pub fn populate_parent_folders( + writer: &mut trap::Writer, + child_label: trap::Label, + path: Option<&Path>, +) { + let mut path = path; + let mut child_label = child_label; + loop { + match path { + None => break, + Some(folder) => { + let (folder_label, fresh) = + writer.global_id(&trap::full_id_for_folder(&normalize_path(folder))); + writer.add_tuple( + "containerparent", + vec![ + trap::Arg::Label(folder_label), + trap::Arg::Label(child_label), + ], + ); + if fresh { + writer.add_tuple( + "folders", + vec![ + trap::Arg::Label(folder_label), + trap::Arg::String(normalize_path(folder)), + ], ); - if fresh { - self.add_tuple( - "folders", - vec![ - Arg::Label(folder_label), - Arg::String(normalize_path(folder)), - ], - ); - path = folder.parent(); - child_label = folder_label; - } else { - break; - } + path = folder.parent(); + child_label = folder_label; + } else { + break; } } } } +} - fn location( - &mut self, - file_label: Label, - start_line: usize, - start_column: usize, - end_line: usize, - end_column: usize, - ) -> Label { - let (loc_label, fresh) = self.global_id(&format!( - "loc,{{{}}},{},{},{},{}", - file_label, start_line, start_column, end_line, end_column - )); - if fresh { - self.add_tuple( - "locations_default", - vec![ - Arg::Label(loc_label), - Arg::Label(file_label), - Arg::Int(start_line), - Arg::Int(start_column), - Arg::Int(end_line), - Arg::Int(end_column), - ], - ); - } - loc_label - } - - fn comment(&mut self, text: String) { - self.trap_output.push(TrapEntry::Comment(text)); - } - - pub fn output(self, writer: &mut dyn Write) -> std::io::Result<()> { - write!(writer, "{}", Program(self.trap_output)) +fn location( + writer: &mut trap::Writer, + file_label: trap::Label, + start_line: usize, + start_column: usize, + end_line: usize, + end_column: usize, +) -> trap::Label { + let (loc_label, fresh) = writer.global_id(&format!( + "loc,{{{}}},{},{},{},{}", + file_label, start_line, start_column, end_line, end_column + )); + if fresh { + writer.add_tuple( + "locations_default", + vec![ + trap::Arg::Label(loc_label), + trap::Arg::Label(file_label), + trap::Arg::Int(start_line), + trap::Arg::Int(start_column), + trap::Arg::Int(end_line), + trap::Arg::Int(end_column), + ], + ); } + loc_label } /// Extracts the source file at `path`, which is assumed to be canonicalized. @@ -163,71 +114,43 @@ pub fn extract( language: Language, language_prefix: &str, schema: &NodeTypeMap, - trap_writer: &mut TrapWriter, + trap_writer: &mut trap::Writer, path: &Path, source: &[u8], ranges: &[Range], ) -> std::io::Result<()> { + let path_str = format!("{}", path.display()); let span = span!( Level::TRACE, "extract", - file = %path.display() + file = %path_str ); let _enter = span.enter(); - info!("extracting: {}", path.display()); + info!("extracting: {}", path_str); let mut parser = Parser::new(); parser.set_language(language).unwrap(); parser.set_included_ranges(ranges).unwrap(); let tree = parser.parse(&source, None).expect("Failed to parse file"); - trap_writer.comment(format!("Auto-generated TRAP file for {}", path.display())); - let file_label = &trap_writer.populate_file(path); - let mut visitor = Visitor { + trap_writer.comment(format!("Auto-generated TRAP file for {}", path_str)); + let file_label = populate_file(trap_writer, path); + let mut visitor = Visitor::new( source, trap_writer, // TODO: should we handle path strings that are not valid UTF8 better? - path: format!("{}", path.display()), - file_label: *file_label, - toplevel_child_counter: 0, - stack: Vec::new(), + &path_str, + file_label, language_prefix, schema, - }; + ); traverse(&tree, &mut visitor); parser.reset(); Ok(()) } -/// Escapes a string for use in a TRAP key, by replacing special characters with -/// HTML entities. -fn escape_key<'a, S: Into>>(key: S) -> Cow<'a, str> { - fn needs_escaping(c: char) -> bool { - matches!(c, '&' | '{' | '}' | '"' | '@' | '#') - } - - let key = key.into(); - if key.contains(needs_escaping) { - let mut escaped = String::with_capacity(2 * key.len()); - for c in key.chars() { - match c { - '&' => escaped.push_str("&"), - '{' => escaped.push_str("{"), - '}' => escaped.push_str("}"), - '"' => escaped.push_str("""), - '@' => escaped.push_str("@"), - '#' => escaped.push_str("#"), - _ => escaped.push(c), - } - } - Cow::Owned(escaped) - } else { - key - } -} - /// Normalizes the path according the common CodeQL specification. Assumes that /// `path` has already been canonicalized using `std::fs::canonicalize`. fn normalize_path(path: &Path) -> String { @@ -267,34 +190,28 @@ fn normalize_path(path: &Path) -> String { } } -fn full_id_for_file(path: &Path) -> String { - format!("{};sourcefile", escape_key(&normalize_path(path))) -} - -fn full_id_for_folder(path: &Path) -> String { - format!("{};folder", escape_key(&normalize_path(path))) -} - struct ChildNode { field_name: Option<&'static str>, - label: Label, + label: trap::Label, type_name: TypeName, } struct Visitor<'a> { /// The file path of the source code (as string) - path: String, + path: &'a str, /// The label to use whenever we need to refer to the `@file` entity of this /// source file. - file_label: Label, + file_label: trap::Label, /// The source code as a UTF-8 byte array source: &'a [u8], - /// A TrapWriter to accumulate trap entries - trap_writer: &'a mut TrapWriter, + /// A trap::Writer to accumulate trap entries + trap_writer: &'a mut trap::Writer, /// A counter for top-level child nodes toplevel_child_counter: usize, - /// Language prefix - language_prefix: &'a str, + /// Language-specific name of the AST info table + ast_node_info_table_name: String, + /// Language-specific name of the tokeninfo table + tokeninfo_table_name: String, /// A lookup table from type name to node types schema: &'a NodeTypeMap, /// A stack for gathering information from child nodes. Whenever a node is @@ -303,27 +220,48 @@ struct Visitor<'a> { /// node the list containing the child data is popped from the stack and /// matched against the dbscheme for the node. If the expectations are met /// the corresponding row definitions are added to the trap_output. - stack: Vec<(Label, usize, Vec)>, + stack: Vec<(trap::Label, usize, Vec)>, } -impl Visitor<'_> { +impl<'a> Visitor<'a> { + fn new( + source: &'a [u8], + trap_writer: &'a mut trap::Writer, + path: &'a str, + file_label: trap::Label, + language_prefix: &str, + schema: &'a NodeTypeMap, + ) -> Visitor<'a> { + Visitor { + path, + file_label, + source, + trap_writer, + toplevel_child_counter: 0, + ast_node_info_table_name: format!("{}_ast_node_info", language_prefix), + tokeninfo_table_name: format!("{}_tokeninfo", language_prefix), + schema, + stack: Vec::new(), + } + } + fn record_parse_error( &mut self, error_message: String, full_error_message: String, - loc: Label, + loc: trap::Label, ) { error!("{}", full_error_message); let id = self.trap_writer.fresh_id(); self.trap_writer.add_tuple( "diagnostics", vec![ - Arg::Label(id), - Arg::Int(40), // severity 40 = error - Arg::String("parse_error".to_string()), - Arg::String(error_message), - Arg::String(full_error_message), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Int(40), // severity 40 = error + trap::Arg::String("parse_error".to_string()), + trap::Arg::String(error_message), + trap::Arg::String(full_error_message), + trap::Arg::Label(loc), ], ); } @@ -335,7 +273,8 @@ impl Visitor<'_> { node: Node, ) { let (start_line, start_column, end_line, end_column) = location_for(self.source, node); - let loc = self.trap_writer.location( + let loc = location( + self.trap_writer, self.file_label, start_line, start_column, @@ -374,7 +313,8 @@ impl Visitor<'_> { } let (id, _, child_nodes) = self.stack.pop().expect("Vistor: empty stack"); let (start_line, start_column, end_line, end_column) = location_for(self.source, node); - let loc = self.trap_writer.location( + let loc = location( + self.trap_writer, self.file_label, start_line, start_column, @@ -402,19 +342,19 @@ impl Visitor<'_> { match &table.kind { EntryKind::Token { kind_id, .. } => { self.trap_writer.add_tuple( - &format!("{}_ast_node_info", self.language_prefix), + &self.ast_node_info_table_name, vec![ - Arg::Label(id), - Arg::Label(parent_id), - Arg::Int(parent_index), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Label(parent_id), + trap::Arg::Int(parent_index), + trap::Arg::Label(loc), ], ); self.trap_writer.add_tuple( - &format!("{}_tokeninfo", self.language_prefix), + &self.tokeninfo_table_name, vec![ - Arg::Label(id), - Arg::Int(*kind_id), + trap::Arg::Label(id), + trap::Arg::Int(*kind_id), sliced_source_arg(self.source, node), ], ); @@ -425,15 +365,15 @@ impl Visitor<'_> { } => { if let Some(args) = self.complex_node(&node, fields, &child_nodes, id) { self.trap_writer.add_tuple( - &format!("{}_ast_node_info", self.language_prefix), + &self.ast_node_info_table_name, vec![ - Arg::Label(id), - Arg::Label(parent_id), - Arg::Int(parent_index), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Label(parent_id), + trap::Arg::Int(parent_index), + trap::Arg::Label(loc), ], ); - let mut all_args = vec![Arg::Label(id)]; + let mut all_args = vec![trap::Arg::Label(id)]; all_args.extend(args); self.trap_writer.add_tuple(table_name, all_args); } @@ -472,9 +412,9 @@ impl Visitor<'_> { node: &Node, fields: &[Field], child_nodes: &[ChildNode], - parent_id: Label, - ) -> Option> { - let mut map: Map<&Option, (&Field, Vec)> = Map::new(); + parent_id: trap::Label, + ) -> Option> { + let mut map: Map<&Option, (&Field, Vec)> = Map::new(); for field in fields { map.insert(&field.name, (field, Vec::new())); } @@ -488,9 +428,9 @@ impl Visitor<'_> { { // We can safely unwrap because type_matches checks the key is in the map. let (int_value, _) = int_mapping.get(&child_node.type_name.kind).unwrap(); - values.push(Arg::Int(*int_value)); + values.push(trap::Arg::Int(*int_value)); } else { - values.push(Arg::Label(child_node.label)); + values.push(trap::Arg::Label(child_node.label)); } } else if field.name.is_some() { let error_message = format!( @@ -569,9 +509,9 @@ impl Visitor<'_> { ); break; } - let mut args = vec![Arg::Label(parent_id)]; + let mut args = vec![trap::Arg::Label(parent_id)]; if *has_index { - args.push(Arg::Int(index)) + args.push(trap::Arg::Int(index)) } args.push(child_value.clone()); self.trap_writer.add_tuple(table_name, args); @@ -625,9 +565,9 @@ impl Visitor<'_> { } // Emit a slice of a source file as an Arg. -fn sliced_source_arg(source: &[u8], n: Node) -> Arg { +fn sliced_source_arg(source: &[u8], n: Node) -> trap::Arg { let range = n.byte_range(); - Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned()) + trap::Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned()) } // Emit a pair of `TrapEntry`s for the provided node, appropriately calibrated. @@ -699,59 +639,6 @@ fn traverse(tree: &Tree, visitor: &mut Visitor) { } } -pub struct Program(Vec); - -impl fmt::Display for Program { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut text = String::new(); - for trap_entry in &self.0 { - text.push_str(&format!("{}\n", trap_entry)); - } - write!(f, "{}", text) - } -} - -enum TrapEntry { - /// Maps the label to a fresh id, e.g. `#123=*`. - FreshId(Label), - /// Maps the label to a key, e.g. `#7=@"foo"`. - MapLabelToKey(Label, String), - /// foo_bar(arg*) - GenericTuple(String, Vec), - Comment(String), -} -impl fmt::Display for TrapEntry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - TrapEntry::FreshId(label) => write!(f, "{}=*", label), - TrapEntry::MapLabelToKey(label, key) => { - write!(f, "{}=@\"{}\"", label, key.replace("\"", "\"\"")) - } - TrapEntry::GenericTuple(name, args) => { - write!(f, "{}(", name)?; - for (index, arg) in args.iter().enumerate() { - if index > 0 { - write!(f, ",")?; - } - write!(f, "{}", arg)?; - } - write!(f, ")") - } - TrapEntry::Comment(line) => write!(f, "// {}", line), - } - } -} - -#[derive(Debug, Copy, Clone)] -// Identifiers of the form #0, #1... -struct Label(u32); - -impl fmt::Display for Label { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "#{:x}", self.0) - } -} - // Numeric indices. #[derive(Debug, Copy, Clone)] struct Index(usize); @@ -761,69 +648,3 @@ impl fmt::Display for Index { write!(f, "{}", self.0) } } - -// Some untyped argument to a TrapEntry. -#[derive(Debug, Clone)] -enum Arg { - Label(Label), - Int(usize), - String(String), -} - -const MAX_STRLEN: usize = 1048576; - -impl fmt::Display for Arg { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Arg::Label(x) => write!(f, "{}", x), - Arg::Int(x) => write!(f, "{}", x), - Arg::String(x) => write!( - f, - "\"{}\"", - limit_string(x, MAX_STRLEN).replace("\"", "\"\"") - ), - } - } -} - -/// Limit the length (in bytes) of a string. If the string's length in bytes is -/// less than or equal to the limit then the entire string is returned. Otherwise -/// the string is sliced at the provided limit. If there is a multi-byte character -/// at the limit then the returned slice will be slightly shorter than the limit to -/// avoid splitting that multi-byte character. -fn limit_string(string: &str, max_size: usize) -> &str { - if string.len() <= max_size { - return string; - } - let p = string.as_bytes(); - let mut index = max_size; - // We want to clip the string at [max_size]; however, the character at that position - // may span several bytes. We need to find the first byte of the character. In UTF-8 - // encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte. - // Therefore we decrement the index as long as there are bytes matching this pattern. - // This ensures we cut the string at the border between one character and another. - while index > 0 && (p[index] & 0b11000000) == 0b10000000 { - index -= 1; - } - &string[0..index] -} - -#[test] -fn limit_string_test() { - assert_eq!("hello", limit_string(&"hello world".to_owned(), 5)); - assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6)); - assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5)); -} - -#[test] -fn escape_key_test() { - assert_eq!("foo!", escape_key("foo!")); - assert_eq!("foo{}", escape_key("foo{}")); - assert_eq!("{}", escape_key("{}")); - assert_eq!("", escape_key("")); - assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb")); - assert_eq!( - "/path/to/foo&{}"@#.rb", - escape_key("/path/to/foo&{}\"@#.rb") - ); -} diff --git a/ql/extractor/src/main.rs b/ql/extractor/src/main.rs index 7f8ab87a7ca..9584304c430 100644 --- a/ql/extractor/src/main.rs +++ b/ql/extractor/src/main.rs @@ -1,49 +1,13 @@ mod extractor; +mod trap; extern crate num_cpus; -use flate2::write::GzEncoder; use rayon::prelude::*; use std::fs; -use std::io::{BufRead, BufWriter}; +use std::io::BufRead; use std::path::{Path, PathBuf}; -enum TrapCompression { - None, - Gzip, -} - -impl TrapCompression { - fn from_env() -> TrapCompression { - match std::env::var("CODEQL_QL_TRAP_COMPRESSION") { - Ok(method) => match TrapCompression::from_string(&method) { - Some(c) => c, - None => { - tracing::error!("Unknown compression method '{}'; using gzip.", &method); - TrapCompression::Gzip - } - }, - // Default compression method if the env var isn't set: - Err(_) => TrapCompression::Gzip, - } - } - - fn from_string(s: &str) -> Option { - match s.to_lowercase().as_ref() { - "none" => Some(TrapCompression::None), - "gzip" => Some(TrapCompression::Gzip), - _ => None, - } - } - - fn extension(&self) -> &str { - match self { - TrapCompression::None => "trap", - TrapCompression::Gzip => "trap.gz", - } - } -} - /** * Gets the number of threads the extractor should use, by reading the * CODEQL_THREADS environment variable and using it as described in the @@ -115,7 +79,7 @@ fn main() -> std::io::Result<()> { .value_of("output-dir") .expect("missing --output-dir"); let trap_dir = PathBuf::from(trap_dir); - let trap_compression = TrapCompression::from_env(); + let trap_compression = trap::Compression::from_env("CODEQL_QL_TRAP_COMPRESSION"); let file_list = matches.value_of("file-list").expect("missing --file-list"); let file_list = fs::File::open(file_list)?; @@ -140,7 +104,7 @@ fn main() -> std::io::Result<()> { let src_archive_file = path_for(&src_archive_dir, &path, ""); let source = std::fs::read(&path)?; let code_ranges = vec![]; - let mut trap_writer = extractor::new_trap_writer(); + let mut trap_writer = trap::Writer::new(); extractor::extract( language, "ql", @@ -152,33 +116,25 @@ fn main() -> std::io::Result<()> { )?; std::fs::create_dir_all(&src_archive_file.parent().unwrap())?; std::fs::copy(&path, &src_archive_file)?; - write_trap(&trap_dir, path, trap_writer, &trap_compression) + write_trap(&trap_dir, path, &trap_writer, trap_compression) }) .expect("failed to extract files"); let path = PathBuf::from("extras"); - let mut trap_writer = extractor::new_trap_writer(); - trap_writer.populate_empty_location(); - write_trap(&trap_dir, path, trap_writer, &trap_compression) + let mut trap_writer = trap::Writer::new(); + extractor::populate_empty_location(&mut trap_writer); + write_trap(&trap_dir, path, &trap_writer, trap_compression) } fn write_trap( trap_dir: &Path, path: PathBuf, - trap_writer: extractor::TrapWriter, - trap_compression: &TrapCompression, + trap_writer: &trap::Writer, + trap_compression: trap::Compression, ) -> std::io::Result<()> { let trap_file = path_for(trap_dir, &path, trap_compression.extension()); std::fs::create_dir_all(&trap_file.parent().unwrap())?; - let trap_file = std::fs::File::create(&trap_file)?; - let mut trap_file = BufWriter::new(trap_file); - match trap_compression { - TrapCompression::None => trap_writer.output(&mut trap_file), - TrapCompression::Gzip => { - let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast()); - trap_writer.output(&mut compressed_writer) - } - } + trap_writer.write_to_file(&trap_file, trap_compression) } fn path_for(dir: &Path, path: &Path, ext: &str) -> PathBuf { diff --git a/ql/extractor/src/trap.rs b/ql/extractor/src/trap.rs new file mode 100644 index 00000000000..35a9b69f255 --- /dev/null +++ b/ql/extractor/src/trap.rs @@ -0,0 +1,275 @@ +use std::borrow::Cow; +use std::fmt; +use std::io::{BufWriter, Write}; +use std::path::Path; + +use flate2::write::GzEncoder; + +pub struct Writer { + /// The accumulated trap entries + trap_output: Vec, + /// A counter for generating fresh labels + counter: u32, + /// cache of global keys + global_keys: std::collections::HashMap, +} + +impl Writer { + pub fn new() -> Writer { + Writer { + counter: 0, + trap_output: Vec::new(), + global_keys: std::collections::HashMap::new(), + } + } + + pub fn fresh_id(&mut self) -> Label { + let label = Label(self.counter); + self.counter += 1; + self.trap_output.push(Entry::FreshId(label)); + label + } + + /// Gets a label that will hold the unique ID of the passed string at import time. + /// This can be used for incrementally importable TRAP files -- use globally unique + /// strings to compute a unique ID for table tuples. + /// + /// Note: You probably want to make sure that the key strings that you use are disjoint + /// for disjoint column types; the standard way of doing this is to prefix (or append) + /// the column type name to the ID. Thus, you might identify methods in Java by the + /// full ID "methods_com.method.package.DeclaringClass.method(argumentList)". + pub fn global_id(&mut self, key: &str) -> (Label, bool) { + if let Some(label) = self.global_keys.get(key) { + return (*label, false); + } + let label = Label(self.counter); + self.counter += 1; + self.global_keys.insert(key.to_owned(), label); + self.trap_output + .push(Entry::MapLabelToKey(label, key.to_owned())); + (label, true) + } + + pub fn add_tuple(&mut self, table_name: &str, args: Vec) { + self.trap_output + .push(Entry::GenericTuple(table_name.to_owned(), args)) + } + + pub fn comment(&mut self, text: String) { + self.trap_output.push(Entry::Comment(text)); + } + + pub fn write_to_file(&self, path: &Path, compression: Compression) -> std::io::Result<()> { + let trap_file = std::fs::File::create(path)?; + match compression { + Compression::None => { + let mut trap_file = BufWriter::new(trap_file); + self.write_trap_entries(&mut trap_file) + } + Compression::Gzip => { + let trap_file = GzEncoder::new(trap_file, flate2::Compression::fast()); + let mut trap_file = BufWriter::new(trap_file); + self.write_trap_entries(&mut trap_file) + } + } + } + + fn write_trap_entries(&self, file: &mut W) -> std::io::Result<()> { + for trap_entry in &self.trap_output { + writeln!(file, "{}", trap_entry)?; + } + std::io::Result::Ok(()) + } +} + +pub enum Entry { + /// Maps the label to a fresh id, e.g. `#123=*`. + FreshId(Label), + /// Maps the label to a key, e.g. `#7=@"foo"`. + MapLabelToKey(Label, String), + /// foo_bar(arg*) + GenericTuple(String, Vec), + Comment(String), +} + +impl fmt::Display for Entry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Entry::FreshId(label) => write!(f, "{}=*", label), + Entry::MapLabelToKey(label, key) => { + write!(f, "{}=@\"{}\"", label, key.replace("\"", "\"\"")) + } + Entry::GenericTuple(name, args) => { + write!(f, "{}(", name)?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(f, ",")?; + } + write!(f, "{}", arg)?; + } + write!(f, ")") + } + Entry::Comment(line) => write!(f, "// {}", line), + } + } +} + +#[derive(Debug, Copy, Clone)] +// Identifiers of the form #0, #1... +pub struct Label(u32); + +impl fmt::Display for Label { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "#{:x}", self.0) + } +} + +// Some untyped argument to a TrapEntry. +#[derive(Debug, Clone)] +pub enum Arg { + Label(Label), + Int(usize), + String(String), +} + +const MAX_STRLEN: usize = 1048576; + +impl fmt::Display for Arg { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Arg::Label(x) => write!(f, "{}", x), + Arg::Int(x) => write!(f, "{}", x), + Arg::String(x) => write!( + f, + "\"{}\"", + limit_string(x, MAX_STRLEN).replace("\"", "\"\"") + ), + } + } +} + +pub struct Program(Vec); + +impl fmt::Display for Program { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut text = String::new(); + for trap_entry in &self.0 { + text.push_str(&format!("{}\n", trap_entry)); + } + write!(f, "{}", text) + } +} + +pub fn full_id_for_file(normalized_path: &str) -> String { + format!("{};sourcefile", escape_key(normalized_path)) +} + +pub fn full_id_for_folder(normalized_path: &str) -> String { + format!("{};folder", escape_key(normalized_path)) +} + +/// Escapes a string for use in a TRAP key, by replacing special characters with +/// HTML entities. +fn escape_key<'a, S: Into>>(key: S) -> Cow<'a, str> { + fn needs_escaping(c: char) -> bool { + matches!(c, '&' | '{' | '}' | '"' | '@' | '#') + } + + let key = key.into(); + if key.contains(needs_escaping) { + let mut escaped = String::with_capacity(2 * key.len()); + for c in key.chars() { + match c { + '&' => escaped.push_str("&"), + '{' => escaped.push_str("{"), + '}' => escaped.push_str("}"), + '"' => escaped.push_str("""), + '@' => escaped.push_str("@"), + '#' => escaped.push_str("#"), + _ => escaped.push(c), + } + } + Cow::Owned(escaped) + } else { + key + } +} + +/// Limit the length (in bytes) of a string. If the string's length in bytes is +/// less than or equal to the limit then the entire string is returned. Otherwise +/// the string is sliced at the provided limit. If there is a multi-byte character +/// at the limit then the returned slice will be slightly shorter than the limit to +/// avoid splitting that multi-byte character. +fn limit_string(string: &str, max_size: usize) -> &str { + if string.len() <= max_size { + return string; + } + let p = string.as_bytes(); + let mut index = max_size; + // We want to clip the string at [max_size]; however, the character at that position + // may span several bytes. We need to find the first byte of the character. In UTF-8 + // encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte. + // Therefore we decrement the index as long as there are bytes matching this pattern. + // This ensures we cut the string at the border between one character and another. + while index > 0 && (p[index] & 0b11000000) == 0b10000000 { + index -= 1; + } + &string[0..index] +} + +#[derive(Clone, Copy)] +pub enum Compression { + None, + Gzip, +} + +impl Compression { + pub fn from_env(var_name: &str) -> Compression { + match std::env::var(var_name) { + Ok(method) => match Compression::from_string(&method) { + Some(c) => c, + None => { + tracing::error!("Unknown compression method '{}'; using gzip.", &method); + Compression::Gzip + } + }, + // Default compression method if the env var isn't set: + Err(_) => Compression::Gzip, + } + } + + pub fn from_string(s: &str) -> Option { + match s.to_lowercase().as_ref() { + "none" => Some(Compression::None), + "gzip" => Some(Compression::Gzip), + _ => None, + } + } + + pub fn extension(&self) -> &str { + match self { + Compression::None => "trap", + Compression::Gzip => "trap.gz", + } + } +} + +#[test] +fn limit_string_test() { + assert_eq!("hello", limit_string(&"hello world".to_owned(), 5)); + assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6)); + assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5)); +} + +#[test] +fn escape_key_test() { + assert_eq!("foo!", escape_key("foo!")); + assert_eq!("foo{}", escape_key("foo{}")); + assert_eq!("{}", escape_key("{}")); + assert_eq!("", escape_key("")); + assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb")); + assert_eq!( + "/path/to/foo&{}"@#.rb", + escape_key("/path/to/foo&{}\"@#.rb") + ); +} diff --git a/ql/ql/src/codeql_ql/ast/Ast.qll b/ql/ql/src/codeql_ql/ast/Ast.qll index fa551b0de83..03a26868657 100644 --- a/ql/ql/src/codeql_ql/ast/Ast.qll +++ b/ql/ql/src/codeql_ql/ast/Ast.qll @@ -21,11 +21,13 @@ private string stringIndexedMember(string name, string index) { class AstNode extends TAstNode { string toString() { result = this.getAPrimaryQlClass() } - /** - * Gets the location of the AST node. - */ + /** Gets the location of the AST node. */ cached - Location getLocation() { + Location getLocation() { result = this.getFullLocation() } // overriden in some subclasses + + /** Gets the location that spans the entire AST node. */ + cached + final Location getFullLocation() { exists(QL::AstNode node | not node instanceof QL::ParExpr | node = toQL(this) and result = node.getLocation() @@ -154,28 +156,42 @@ class TopLevel extends TTopLevel, AstNode { override QLDoc getQLDoc() { result = this.getMember(0) } } -class QLDoc extends TQLDoc, AstNode { +abstract class Comment extends AstNode, TComment { + abstract string getContents(); +} + +class QLDoc extends TQLDoc, Comment { QL::Qldoc qldoc; QLDoc() { this = TQLDoc(qldoc) } - string getContents() { result = qldoc.getValue() } + override string getContents() { result = qldoc.getValue() } override string getAPrimaryQlClass() { result = "QLDoc" } override AstNode getParent() { result.getQLDoc() = this } } -class BlockComment extends TBlockComment, AstNode { +class BlockComment extends TBlockComment, Comment { QL::BlockComment comment; BlockComment() { this = TBlockComment(comment) } - string getContents() { result = comment.getValue() } + override string getContents() { result = comment.getValue() } override string getAPrimaryQlClass() { result = "BlockComment" } } +class LineComment extends TLineComment, Comment { + QL::LineComment comment; + + LineComment() { this = TLineComment(comment) } + + override string getContents() { result = comment.getValue() } + + override string getAPrimaryQlClass() { result = "LineComment" } +} + /** * The `from, where, select` part of a QL query. */ @@ -434,6 +450,8 @@ class ClasslessPredicate extends TClasslessPredicate, Predicate, ModuleDeclarati ClasslessPredicate() { this = TClasslessPredicate(pred) } + override Location getLocation() { result = pred.getName().getLocation() } + /** * Gets the aliased value if this predicate is an alias * E.g. for `predicate foo = Module::bar/2;` gets `Module::bar/2`. @@ -484,6 +502,8 @@ class ClassPredicate extends TClassPredicate, Predicate { ClassPredicate() { this = TClassPredicate(pred) } + override Location getLocation() { result = pred.getName().getLocation() } + override string getName() { result = pred.getName().getValue() } override Formula getBody() { toQL(result) = pred.getChild(_).(QL::Body).getChild() } @@ -682,7 +702,7 @@ class TypeExpr extends TType, TypeRef { // resolve type resolveTypeExpr(this, result) or - // if it resolves to a module + // if it resolves to a module, exists(FileOrModule mod | resolveModuleRef(this, mod) | result = mod.toType()) } @@ -701,6 +721,8 @@ class Module extends TModule, ModuleDeclaration { Module() { this = TModule(mod) } + override Location getLocation() { result = mod.getName().getLocation() } + override string getAPrimaryQlClass() { result = "Module" } override string getName() { result = mod.getName().getChild().getValue() } @@ -784,6 +806,8 @@ class Class extends TClass, TypeDeclaration, ModuleDeclaration { Class() { this = TClass(cls) } + override Location getLocation() { result = cls.getName().getLocation() } + override string getAPrimaryQlClass() { result = "Class" } override string getName() { result = cls.getName().getValue() } @@ -871,6 +895,8 @@ class NewType extends TNewType, TypeDeclaration, ModuleDeclaration { NewType() { this = TNewType(type) } + override Location getLocation() { result = type.getName().getLocation() } + override string getName() { result = type.getName().getValue() } override string getAPrimaryQlClass() { result = "NewType" } @@ -896,6 +922,8 @@ class NewTypeBranch extends TNewTypeBranch, Predicate, TypeDeclaration { NewTypeBranch() { this = TNewTypeBranch(branch) } + override Location getLocation() { result = branch.getName().getLocation() } + override string getAPrimaryQlClass() { result = "NewTypeBranch" } override string getName() { result = branch.getName().getValue() } @@ -1167,7 +1195,7 @@ class Import extends TImport, ModuleMember, TypeRef { */ string getImportString() { exists(string selec | - not exists(getSelectionName(_)) and selec = "" + not exists(this.getSelectionName(_)) and selec = "" or selec = "::" + strictconcat(int i, string q | q = this.getSelectionName(i) | q, "::" order by i) @@ -2217,9 +2245,7 @@ class ModuleExpr extends TModuleExpr, TypeRef { or not exists(me.getName()) and result = me.getChild().(QL::SimpleId).getValue() or - exists(QL::ModuleInstantiation instantiation | instantiation.getParent() = me | - result = instantiation.getName().getChild().getValue() - ) + result = me.getAFieldOrChild().(QL::ModuleInstantiation).getName().getChild().getValue() } /** @@ -2254,9 +2280,7 @@ class ModuleExpr extends TModuleExpr, TypeRef { * The result is either a `PredicateExpr` or a `TypeExpr`. */ SignatureExpr getArgument(int i) { - exists(QL::ModuleInstantiation instantiation | instantiation.getParent() = me | - result.toQL() = instantiation.getChild(i) - ) + result.toQL() = me.getAFieldOrChild().(QL::ModuleInstantiation).getChild(i) } } diff --git a/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll b/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll index 9448fe71240..ce5cd352dd8 100644 --- a/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll +++ b/ql/ql/src/codeql_ql/ast/internal/AstNodes.qll @@ -7,6 +7,7 @@ newtype TAstNode = TTopLevel(QL::Ql file) or TQLDoc(QL::Qldoc qldoc) or TBlockComment(QL::BlockComment comment) or + TLineComment(QL::LineComment comment) or TClasslessPredicate(QL::ClasslessPredicate pred) or TVarDecl(QL::VarDecl decl) or TFieldDecl(QL::Field field) or @@ -89,6 +90,8 @@ class TYamlNode = TYamlCommemt or TYamlEntry or TYamlKey or TYamlListitem or TYa class TSignatureExpr = TPredicateExpr or TType; +class TComment = TQLDoc or TBlockComment or TLineComment; + /** DEPRECATED: Alias for TYamlNode */ deprecated class TYAMLNode = TYamlNode; @@ -154,6 +157,8 @@ QL::AstNode toQL(AST::AstNode n) { or n = TBlockComment(result) or + n = TLineComment(result) + or n = TClasslessPredicate(result) or n = TVarDecl(result) diff --git a/ql/ql/src/codeql_ql/ast/internal/Module.qll b/ql/ql/src/codeql_ql/ast/internal/Module.qll index b487c98cc7e..8c1e6189e0a 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Module.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Module.qll @@ -78,7 +78,7 @@ private class Folder_ extends ContainerOrModule, TFolder { override ContainerOrModule getEnclosing() { result = TFolder(f.getParentContainer()) and - // if this the the root, then we stop. + // if this the root, then we stop. not exists(f.getFile("qlpack.yml")) } @@ -218,6 +218,20 @@ private module Cached { not exists(me.(ModuleExpr).getQualifier()) and exists(ContainerOrModule enclosing, string name | resolveModuleRefHelper(me, enclosing, name) | definesModule(enclosing, name, m, _) + ) and + ( + not me instanceof TypeExpr + or + // remove some spurious results that can happen with `TypeExpr` + me instanceof TypeExpr and + m instanceof Module_ and // TypeExpr can only resolve to a Module, and only in some scenarios + ( + // only possible if this is inside a moduleInstantiation. + me = any(ModuleExpr mod).getArgument(_).asType() + or + // or if it's a parameter to a parameterized module + me = any(SignatureExpr sig, Module mod | mod.hasParameter(_, _, sig) | sig).asType() + ) ) or exists(FileOrModule mid | @@ -229,7 +243,8 @@ private module Cached { pragma[noinline] private predicate resolveModuleRefHelper(TypeRef me, ContainerOrModule enclosing, string name) { enclosing = getEnclosingModule(me).getEnclosing*() and - name = [me.(ModuleExpr).getName(), me.(TypeExpr).getClassName()] + name = [me.(ModuleExpr).getName(), me.(TypeExpr).getClassName()] and + (not me instanceof ModuleExpr or not enclosing instanceof Folder_) // module expressions are not imports, so they can't resolve to a file (which is contained in a folder). } } @@ -248,7 +263,7 @@ boolean getPublicBool(AstNode n) { /** * Holds if `container` defines module `m` with name `name`. * - * `m` may be defined either directly or through `import`s. + * `m` may be defined either directly or through imports. */ private predicate definesModule( ContainerOrModule container, string name, ContainerOrModule m, boolean public @@ -332,7 +347,19 @@ module ModConsistency { * } */ - query predicate noName(Module mod) { not exists(mod.getName()) } + query predicate noName(AstNode mod) { + mod instanceof Module and + not exists(mod.(Module).getName()) + or + mod instanceof ModuleExpr and + not exists(mod.(ModuleExpr).getName()) + } - query predicate nonUniqueName(Module mod) { count(mod.getName()) >= 2 } + query predicate nonUniqueName(AstNode mod) { + mod instanceof Module and + count(mod.(Module).getName()) >= 2 + or + mod instanceof ModuleExpr and + count(mod.(ModuleExpr).getName()) >= 2 + } } diff --git a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll index eb7e298b7cd..d419534788b 100644 --- a/ql/ql/src/codeql_ql/ast/internal/Predicate.qll +++ b/ql/ql/src/codeql_ql/ast/internal/Predicate.qll @@ -31,7 +31,8 @@ private predicate definesPredicate( name = alias.getName() and resolvePredicateExpr(alias.getAlias(), p) and public = getPublicBool(alias) and - arity = alias.getArity() + arity = alias.getArity() and + arity = p.getArity() ) ) } @@ -48,15 +49,18 @@ private module Cached { m = pe.getQualifier().getResolvedModule() and public = true | - definesPredicate(m, pe.getName(), p.getArity(), p, public) + definesPredicate(m, pe.getName(), p.getArity(), p, public) and + p.getArity() = pe.getArity() ) } private predicate resolvePredicateCall(PredicateCall pc, PredicateOrBuiltin p) { + // calls to class methods exists(Class c, ClassType t | c = pc.getParent*() and t = c.getType() and - p = t.getClassPredicate(pc.getPredicateName(), pc.getNumberOfArguments()) + p = t.getClassPredicate(pc.getPredicateName(), pc.getNumberOfArguments()) and + not exists(pc.getQualifier()) // no module qualifier, because then it's not a call to a class method. ) or exists(FileOrModule m, boolean public | @@ -85,22 +89,47 @@ private module Cached { ) } + /** + * Holds if `mc` is a `this.method()` call to a predicate defined in the same class. + * helps avoid spuriously resolving to predicates in super-classes. + */ + private predicate resolveSelfClassCalls(MemberCall mc, PredicateOrBuiltin p) { + exists(Class c | + mc.getBase() instanceof ThisAccess and + c = mc.getEnclosingPredicate().getParent() and + p = c.getClassPredicate(mc.getMemberName()) and + p.getArity() = mc.getNumberOfArguments() + ) + } + private predicate resolveMemberCall(MemberCall mc, PredicateOrBuiltin p) { + resolveSelfClassCalls(mc, p) + or + not resolveSelfClassCalls(mc, _) and exists(Type t | t = mc.getBase().getType() and p = t.getClassPredicate(mc.getMemberName(), mc.getNumberOfArguments()) ) or // super calls - and `this.method()` calls in charpreds. (Basically: in charpreds there is no difference between super and this.) - exists(AstNode sup, ClassType type, Type supertype | + exists(AstNode sup, Type supertype | sup instanceof Super or sup.(ThisAccess).getEnclosingPredicate() instanceof CharPred | mc.getBase() = sup and - sup.getEnclosingPredicate().getParent().(Class).getType() = type and - supertype in [type.getASuperType(), type.getAnInstanceofType()] and - p = supertype.getClassPredicate(mc.getMemberName(), mc.getNumberOfArguments()) + p = supertype.getClassPredicate(mc.getMemberName(), mc.getNumberOfArguments()) and + ( + // super.method() + not exists(mc.getSuperType()) and + exists(ClassType type | + sup.getEnclosingPredicate().getParent().(Class).getType() = type and + supertype in [type.getASuperType(), type.getAnInstanceofType()] + ) + or + // Class.super.method() + supertype = mc.getSuperType().getResolvedType() + ) ) } @@ -172,24 +201,29 @@ module PredConsistency { } query predicate multipleResolvePredicateExpr(PredicateExpr pe, int c, ClasslessPredicate p) { - c = strictcount(ClasslessPredicate p0 | resolvePredicateExpr(pe, p0)) and + c = + strictcount(ClasslessPredicate p0 | + resolvePredicateExpr(pe, p0) and + // aliases are expected to resolve to multiple. + not exists(p0.getAlias()) + ) and c > 1 and resolvePredicateExpr(pe, p) } - // This can happen with parametarized modules - /* - * query predicate multipleResolveCall(Call call, int c, PredicateOrBuiltin p) { - * c = - * strictcount(PredicateOrBuiltin p0 | - * resolveCall(call, p0) and - * // aliases are expected to resolve to multiple. - * not exists(p0.(ClasslessPredicate).getAlias()) and - * // overridden predicates may have multiple targets - * not p0.(ClassPredicate).isOverride() - * ) and - * c > 1 and - * resolveCall(call, p) - * } - */ + query predicate multipleResolveCall(Call call, int c, PredicateOrBuiltin p) { + c = + strictcount(PredicateOrBuiltin p0 | + resolveCall(call, p0) and + // aliases are expected to resolve to multiple. + not exists(p0.(ClasslessPredicate).getAlias()) and + // overridden predicates may have multiple targets + not p0.(ClassPredicate).isOverride() and + not p0 instanceof Relation // <- DB relations resolve to both a relation and a predicate. + ) and + c > 1 and + resolveCall(call, p) and + // parameterized modules are expected to resolve to multiple. + not exists(Predicate sig | not exists(sig.getBody()) and resolveCall(call, sig)) } +} diff --git a/ql/ql/src/codeql_ql/dataflow/DataFlow.qll b/ql/ql/src/codeql_ql/dataflow/DataFlow.qll index da8bc1da837..c9043416bae 100644 --- a/ql/ql/src/codeql_ql/dataflow/DataFlow.qll +++ b/ql/ql/src/codeql_ql/dataflow/DataFlow.qll @@ -204,7 +204,7 @@ class SuperNode extends LocalFlow::TSuperNode { Node getANode() { LocalFlow::getRepr(result) = repr } /** Gets an AST node from any of the nodes in this super node. */ - AstNode asAstNode() { result = getANode().asAstNode() } + AstNode asAstNode() { result = this.getANode().asAstNode() } /** * Gets a single node from this super node. @@ -214,23 +214,25 @@ class SuperNode extends LocalFlow::TSuperNode { * - An `AstNodeNode` is preferred over other nodes. * - A node occurring earlier is preferred over one occurring later. */ - Node getArbitraryRepr() { result = min(Node n | n = getANode() | n order by getInternalId(n)) } + Node getArbitraryRepr() { + result = min(Node n | n = this.getANode() | n order by getInternalId(n)) + } /** * Gets the predicate containing all nodes that are part of this super node. */ - Predicate getEnclosingPredicate() { result = getANode().getEnclosingPredicate() } + Predicate getEnclosingPredicate() { result = this.getANode().getEnclosingPredicate() } /** Gets a string representation of this super node. */ string toString() { exists(int c | - c = strictcount(getANode()) and - result = "Super node of " + c + " nodes in " + getEnclosingPredicate().getName() + c = strictcount(this.getANode()) and + result = "Super node of " + c + " nodes in " + this.getEnclosingPredicate().getName() ) } /** Gets the location of an arbitrary node in this super node. */ - Location getLocation() { result = getArbitraryRepr().getLocation() } + Location getLocation() { result = this.getArbitraryRepr().getLocation() } /** Gets any member call whose receiver is in the same super node. */ MemberCall getALocalMemberCall() { superNode(result.getBase()) = this } @@ -287,7 +289,7 @@ class SuperNode extends LocalFlow::TSuperNode { cached private string getAStringValue(Tracker t) { t.start() and - result = asAstNode().(String).getValue() + result = this.asAstNode().(String).getValue() or exists(SuperNode pred, Tracker t2 | this = pred.track(t2, t) and diff --git a/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll b/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll index 8974560fd59..e2eaff9c5ca 100644 --- a/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll +++ b/ql/ql/src/codeql_ql/style/DeadCodeQuery.qll @@ -203,7 +203,7 @@ private AstNode classUnion() { private AstNode benign() { not result.getLocation().getFile().getExtension() = ["ql", "qll"] or // ignore dbscheme files - result instanceof BlockComment or + result instanceof Comment or not exists(result.toString()) or // <- invalid code // cached-stages pattern result.(Module).getAMember().(ClasslessPredicate).getName() = "forceStage" or diff --git a/ql/ql/src/codeql_ql/style/TypoDatabase.qll b/ql/ql/src/codeql_ql/style/TypoDatabase.qll index aad43d9d0cc..fadc1b8af75 100644 --- a/ql/ql/src/codeql_ql/style/TypoDatabase.qll +++ b/ql/ql/src/codeql_ql/style/TypoDatabase.qll @@ -5793,6 +5793,8 @@ predicate typos(string wrong, string right) { or wrong = "paramters" and right = "parameters" or + wrong = "parametarized" and right = "parameterized" + or wrong = "paranthesis" and right = "parenthesis" or wrong = "paraphenalia" and right = "paraphernalia" diff --git a/ql/ql/src/queries/bugs/InconsistentDeprecation.ql b/ql/ql/src/queries/bugs/InconsistentDeprecation.ql new file mode 100644 index 00000000000..366d2daa4f5 --- /dev/null +++ b/ql/ql/src/queries/bugs/InconsistentDeprecation.ql @@ -0,0 +1,24 @@ +/** + * @name Inconsistent deprecation + * @description A deprecated predicate that overrides a non-deprecated predicate is an indication that the super-predicate should be deprecated. + * @kind problem + * @problem.severity warning + * @id ql/inconsistent-deprecation + * @tags correctness + * maintanability + * @precision very-high + */ + +import ql + +predicate overrides(ClassPredicate sub, ClassPredicate sup, string description, string overrides) { + sub.overrides(sup) and description = "predicate" and overrides = "predicate" +} + +from AstNode sub, AstNode sup, string description, string overrides +where + overrides(sub, sup, description, overrides) and + sub.hasAnnotation("deprecated") and + not sup.hasAnnotation("deprecated") +select sub, "This deprecated " + description + " overrides $@. Consider deprecating both.", sup, + "a non-deprecated " + overrides diff --git a/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql b/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql index 7f95fec935e..8691aa2168c 100644 --- a/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql +++ b/ql/ql/src/queries/diagnostics/EmptyConsistencies.ql @@ -22,6 +22,11 @@ where or PredConsistency::noResolvePredicateExpr(node) and msg = "PredConsistency::noResolvePredicateExpr" or + PredConsistency::multipleResolveCall(node, _, _) and msg = "PredConsistency::multipleResolveCall" + or + PredConsistency::multipleResolvePredicateExpr(node, _, _) and + msg = "PredConsistency::multipleResolvePredicateExpr" + or TypeConsistency::exprNoType(node) and msg = "TypeConsistency::exprNoType" or TypeConsistency::varDefNoType(node) and msg = "TypeConsistency::varDefNoType" diff --git a/ql/ql/src/queries/performance/MissingNomagic.ql b/ql/ql/src/queries/performance/MissingNomagic.ql index 13084bf6ad6..ebab0e7b7e5 100644 --- a/ql/ql/src/queries/performance/MissingNomagic.ql +++ b/ql/ql/src/queries/performance/MissingNomagic.ql @@ -57,7 +57,7 @@ class CandidatePredicate extends Predicate { this.getName() .regexpCapture("(.+)" + ["0", "helper", "aux", "cand", "Helper", "Aux", "Cand"], 1) or - // Or this this predicate is named "foo02" and `pred` is named "foo01". + // Or this predicate is named "foo02" and `pred` is named "foo01". exists(int n, string name | hasNameWithNumberSuffix(pred, name, n) and hasNameWithNumberSuffix(this, name, n - 1) diff --git a/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql b/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql index 2317cdc80c4..c26b47554fe 100644 --- a/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql +++ b/ql/ql/src/queries/performance/VarUnusedInDisjunct.ql @@ -119,12 +119,14 @@ class EffectiveDisjunction extends AstNode { * Holds if `disj` only uses `var` in one of its branches. */ pragma[noinline] -predicate onlyUseInOneBranch(EffectiveDisjunction disj, VarDef var) { +predicate onlyUseInOneBranch(EffectiveDisjunction disj, VarDef var, AstNode notBoundIn) { alwaysBindsVar(var, disj.getLeft()) and - not alwaysBindsVar(var, disj.getRight()) + not alwaysBindsVar(var, disj.getRight()) and + notBoundIn = disj.getRight() or not alwaysBindsVar(var, disj.getLeft()) and - alwaysBindsVar(var, disj.getRight()) + alwaysBindsVar(var, disj.getRight()) and + notBoundIn = disj.getLeft() } /** @@ -170,7 +172,7 @@ class EffectiveConjunction extends AstNode { predicate varIsAlwaysBound(VarDef var, AstNode node) { // base case alwaysBindsVar(var, node) and - onlyUseInOneBranch(_, var) // <- manual magic + onlyUseInOneBranch(_, var, _) // <- manual magic or // recursive cases exists(AstNode parent | node.getParent() = parent | varIsAlwaysBound(var, parent)) @@ -194,8 +196,8 @@ predicate varIsAlwaysBound(VarDef var, AstNode node) { * Holds if `disj` only uses `var` in one of its branches. * And we should report it as being a bad thing. */ -predicate badDisjunction(EffectiveDisjunction disj, VarDef var) { - onlyUseInOneBranch(disj, var) and +predicate badDisjunction(EffectiveDisjunction disj, VarDef var, AstNode notBoundIn) { + onlyUseInOneBranch(disj, var, notBoundIn) and // it's fine if it's always bound further up not varIsAlwaysBound(var, disj) and // none() on one side makes everything fine. (this happens, it's a type-system hack) @@ -213,9 +215,9 @@ predicate badDisjunction(EffectiveDisjunction disj, VarDef var) { not isTinyAssignment(disj.getAnOperand()) } -from EffectiveDisjunction disj, VarDef var, string type +from EffectiveDisjunction disj, VarDef var, AstNode notBoundIn, string type where - badDisjunction(disj, var) and - not badDisjunction(disj.getParent(), var) and // avoid duplicate reporting of the same error + badDisjunction(disj, var, notBoundIn) and + not badDisjunction(disj.getParent(), var, _) and // avoid duplicate reporting of the same error if var.getParent() instanceof FieldDecl then type = "field" else type = "variable" select disj, "The $@ is only used in one side of disjunct.", var, type + " " + var.getName() diff --git a/ql/ql/src/queries/reports/FrameworkCoverage.ql b/ql/ql/src/queries/reports/FrameworkCoverage.ql index 7f5eb7f5310..f394a5a0091 100644 --- a/ql/ql/src/queries/reports/FrameworkCoverage.ql +++ b/ql/ql/src/queries/reports/FrameworkCoverage.ql @@ -18,11 +18,13 @@ class PackageImportCall extends PredicateCall { PackageImportCall() { this.getQualifier().getName() = ["API", "DataFlow"] and this.getPredicateName() = ["moduleImport", "moduleMember"] and - not isExcludedFile(getLocation().getFile()) + not isExcludedFile(this.getLocation().getFile()) } /** Gets the name of a package referenced by this call */ - string getAPackageName() { result = DataFlow::superNode(getArgument(0)).getAStringValueNoCall() } + string getAPackageName() { + result = DataFlow::superNode(this.getArgument(0)).getAStringValueNoCall() + } } /** Gets a reference to `package` or any transitive member thereof. */ diff --git a/ql/ql/src/queries/style/LibraryAnnotation.ql b/ql/ql/src/queries/style/LibraryAnnotation.ql index cf4a4bc8232..99b80f97fb9 100644 --- a/ql/ql/src/queries/style/LibraryAnnotation.ql +++ b/ql/ql/src/queries/style/LibraryAnnotation.ql @@ -10,6 +10,9 @@ import ql -from AstNode n -where n.hasAnnotation("library") and not n.hasAnnotation("deprecated") +from AstNode n, Annotation library +where + library = n.getAnAnnotation() and + library.getName() = "library" and + not n.hasAnnotation("deprecated") select n, "Don't use the library annotation." diff --git a/ql/ql/src/queries/style/MissingParameterInQlDoc.ql b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql new file mode 100644 index 00000000000..e0ac524e7ee --- /dev/null +++ b/ql/ql/src/queries/style/MissingParameterInQlDoc.ql @@ -0,0 +1,94 @@ +/** + * @name Missing QLDoc for parameter + * @description It is suspicious when a predicate has a parameter that is + * unmentioned in the qldoc, and the qldoc contains a + * code-fragment mentioning a non-parameter. + * @kind problem + * @problem.severity warning + * @precision high + * @id ql/missing-parameter-qldoc + * @tags maintainability + */ + +import ql + +/** + * Gets a fragment enclosed in backticks (`) from a QLDoc of the predicate `p`. + * Skips code-blocks that are triple-quoted. + */ +private string getACodeFragment(Predicate p) { + result = p.getQLDoc().getContents().regexpFind("`([^`]*)`(?!`)", _, _) +} + +/** Gets a parameter name from `p`. */ +private string getAParameterName(Predicate p) { result = p.getParameter(_).getName() } + +/** + * Gets the name of a parameter of `p` that is mentioned in any way in the QLDoc of `p`. + * Also includes names that are mentioned in non-code fragments. + */ +private string getADocumentedParameter(Predicate p) { + result = p.getQLDoc().getContents().regexpFind("\\b\\w[\\w_]*\\b", _, _) and + result.toLowerCase() = getAParameterName(p).toLowerCase() +} + +/** + * Get something that looks like a parameter name from the QLDoc, + * but which is not a parameter of `p`. + */ +private string getAMentionedNonParameter(Predicate p) { + exists(string fragment | fragment = getACodeFragment(p) | + result = fragment.substring(1, fragment.length() - 1) + ) and + result.regexpMatch("^[a-z]\\w+$") and + not result.toLowerCase() = getAParameterName(p).toLowerCase() and + // keywords + not result = + [ + "true", "false", "NaN", "this", "forall", "exists", "null", "break", "return", "not", "if", + "then", "else", "import", "async" + ] and + not result = any(Aggregate a).getKind() and // min, max, sum, count, etc. + not result = getMentionedThings(p.getLocation().getFile()) and + not result = any(Annotation a).getName() and // private, final, etc. + not result = any(Annotation a).getArgs(_).getValue() and // noinline, etc. + // variables inside the predicate are also fine + not result = any(VarDecl var | var.getEnclosingPredicate() = p).getName() +} + +/** Gets the names of all predicates and string constants that are mentioned in `file`. */ +pragma[noinline] +private string getMentionedThings(File file) { + // predicates get mentioned all the time, it's fine. + result = any(Predicate pred | pred.getLocation().getFile() = file).getName() or + result = any(Call c | c.getLocation().getFile() = file).getTarget().getName() or + result = any(String s | s.getLocation().getFile() = file).getValue() +} + +/** Gets a parameter name from `p` that is not mentioned in the qldoc. */ +private string getAnUndocumentedParameter(Predicate p) { + result = getAParameterName(p) and + not result.toLowerCase() = getADocumentedParameter(p).toLowerCase() and + not result = ["config", "conf", "cfg", "t", "t2"] and // DataFlow configurations / type-trackers are often undocumented, and that's fine. + not ( + // "the given" often refers to the first parameter. + p.getQLDoc().getContents().regexpMatch("(?s).*\\bthe given\\b.*") and + result = p.getParameter(0).getName() + ) +} + +/** Gets the one string containing the undocumented parameters from `p` */ +private string getUndocumentedParameters(Predicate p) { + result = strictconcat(string param | param = getAnUndocumentedParameter(p) | param, ", or ") +} + +/** Gets the parameter-like names mentioned in the QLDoc of `p` that are not parameters. */ +private string getMentionedNonParameters(Predicate p) { + result = strictconcat(string param | param = getAMentionedNonParameter(p) | param, ", and ") +} + +from Predicate p +where not p.getLocation().getFile().getBaseName() in ["Aliases.qll", "TreeSitter.qll"] // these are OK +select p, + "The QLDoc has no documentation for " + getUndocumentedParameters(p) + ", but the QLDoc mentions " + + getMentionedNonParameters(p) diff --git a/ql/ql/src/queries/style/RepeatedWord.ql b/ql/ql/src/queries/style/RepeatedWord.ql new file mode 100644 index 00000000000..cbbed4668bd --- /dev/null +++ b/ql/ql/src/queries/style/RepeatedWord.ql @@ -0,0 +1,25 @@ +/** + * @name Comment has repeated word + * @description Comment has repeated word. + * @kind problem + * @problem.severity warning + * @id ql/repeated-word + * @precision very-high + */ + +import ql + +/** Gets a word used in `node` */ +string getWord(Comment node) { result = node.getContents().regexpFind("\\b[A-Za-z]+\\b", _, _) } + +Comment hasRepeatedWord(string word) { + word = getWord(result) and + result.getContents().regexpMatch(".*[\\s]" + word + "\\s+" + word + "[\\s.,].*") +} + +from Comment comment, string word +where + comment = hasRepeatedWord(word) and + // lots of these, and I can't just change old dbschemes. + not (word = "type" and comment.getLocation().getFile().getExtension() = "dbscheme") +select comment, "The comment repeats " + word + "." diff --git a/ql/ql/src/queries/style/UseInstanceofExtension.ql b/ql/ql/src/queries/style/UseInstanceofExtension.ql index 2e7b8e3de80..da27e5cbb47 100644 --- a/ql/ql/src/queries/style/UseInstanceofExtension.ql +++ b/ql/ql/src/queries/style/UseInstanceofExtension.ql @@ -18,4 +18,4 @@ where usesFieldBasedInstanceof(c, any(TypeExpr te | te.getResolvedType() = type), _, _) ) and message = "consider defining $@ as non-extending subtype of $@" -select c, message, c, c.getName(), type, type.getName() +select c, message, c, c.getName(), type.getDeclaration(), type.getName() diff --git a/ql/ql/test/callgraph/MultiResolve.qll b/ql/ql/test/callgraph/MultiResolve.qll new file mode 100644 index 00000000000..dce88d01689 --- /dev/null +++ b/ql/ql/test/callgraph/MultiResolve.qll @@ -0,0 +1,58 @@ +predicate foo(int a, int b) { + a = 2 and + b = 2 +} + +predicate foo(int a, int b, int c) { + a = 2 and + b = 2 and + c = 2 +} + +predicate myFoo = foo/2; + +predicate test(int i) { myFoo(i, i) } // <- should only resolve to the `foo` with 2 arguments (and the `myFoo` alias). + +module MyMod { + predicate bar() { any() } + + class Bar extends int { + Bar() { this = 42 } + + predicate bar() { + MyMod::bar() // <- should resolve to the module's predicate. + } + } +} + +class Super1 extends int { + Super1() { this = 42 } + + predicate foo() { any() } +} + +class Super2 extends int { + Super2() { this = 42 } + + predicate foo() { none() } +} + +class Sub extends Super1, Super2 { + override predicate foo() { Super1.super.foo() } // <- should resolve to Super1::foo() +} + +module Foo { + predicate foo() { any() } +} + +predicate test() { + Foo::foo() // <- should resolve to `foo` from the module above, and not from the `Foo.qll` file. +} + +class Sub2 extends Super1, Super2 { + override predicate foo() { Super2.super.foo() } // <- should resolve to Super2::foo() + + predicate test() { + this.foo() // <- should resolve to only the above `foo` predicate, but currently it resolves to that, and all the overrides [INCONSISTENCY] + } +} diff --git a/ql/ql/test/callgraph/callgraph.expected b/ql/ql/test/callgraph/callgraph.expected index 97c84034602..7b86b05953b 100644 --- a/ql/ql/test/callgraph/callgraph.expected +++ b/ql/ql/test/callgraph/callgraph.expected @@ -1,47 +1,53 @@ getTarget -| Bar.qll:5:38:5:47 | PredicateCall | Bar.qll:8:3:8:31 | ClasslessPredicate snapshot | -| Bar.qll:24:12:24:32 | MemberCall | Bar.qll:19:3:19:47 | ClassPredicate getParameter | -| Bar.qll:26:12:26:31 | MemberCall | Bar.qll:19:3:19:47 | ClassPredicate getParameter | -| Bar.qll:30:12:30:32 | MemberCall | Bar.qll:19:3:19:47 | ClassPredicate getParameter | -| Baz.qll:8:18:8:44 | MemberCall | Baz.qll:4:3:4:37 | ClassPredicate getImportedPath | -| Foo.qll:5:26:5:30 | PredicateCall | Foo.qll:3:1:3:26 | ClasslessPredicate foo | -| Foo.qll:10:21:10:25 | PredicateCall | Foo.qll:8:3:8:28 | ClassPredicate bar | -| Foo.qll:14:30:14:40 | MemberCall | Foo.qll:10:3:10:27 | ClassPredicate baz | -| Foo.qll:17:27:17:42 | MemberCall | Foo.qll:8:3:8:28 | ClassPredicate bar | -| Foo.qll:29:5:29:16 | PredicateCall | Foo.qll:20:3:20:54 | ClasslessPredicate myThing2 | -| Foo.qll:29:5:29:16 | PredicateCall | Foo.qll:26:3:26:32 | ClasslessPredicate alias2 | -| Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:22:3:22:32 | ClasslessPredicate myThing0 | -| Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:24:3:24:32 | ClasslessPredicate alias0 | +| Bar.qll:5:38:5:47 | PredicateCall | Bar.qll:8:7:8:14 | ClasslessPredicate snapshot | +| Bar.qll:24:12:24:32 | MemberCall | Bar.qll:19:7:19:18 | ClassPredicate getParameter | +| Bar.qll:26:12:26:31 | MemberCall | Bar.qll:19:7:19:18 | ClassPredicate getParameter | +| Bar.qll:30:12:30:32 | MemberCall | Bar.qll:19:7:19:18 | ClassPredicate getParameter | +| Baz.qll:8:18:8:44 | MemberCall | Baz.qll:4:10:4:24 | ClassPredicate getImportedPath | +| Foo.qll:5:26:5:30 | PredicateCall | Foo.qll:3:11:3:13 | ClasslessPredicate foo | +| Foo.qll:10:21:10:25 | PredicateCall | Foo.qll:8:13:8:15 | ClassPredicate bar | +| Foo.qll:14:30:14:40 | MemberCall | Foo.qll:10:13:10:15 | ClassPredicate baz | +| Foo.qll:17:27:17:42 | MemberCall | Foo.qll:8:13:8:15 | ClassPredicate bar | +| Foo.qll:29:5:29:16 | PredicateCall | Foo.qll:20:13:20:20 | ClasslessPredicate myThing2 | +| Foo.qll:29:5:29:16 | PredicateCall | Foo.qll:26:13:26:18 | ClasslessPredicate alias2 | +| Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:22:13:22:20 | ClasslessPredicate myThing0 | +| Foo.qll:31:5:31:12 | PredicateCall | Foo.qll:24:13:24:18 | ClasslessPredicate alias0 | | Foo.qll:36:36:36:65 | MemberCall | file://:0:0:0:0 | replaceAll | | Foo.qll:38:39:38:67 | MemberCall | file://:0:0:0:0 | regexpCapture | -| Overrides.qll:8:30:8:39 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | -| Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | -| Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:14:12:14:43 | ClassPredicate bar | -| Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | -| Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:22:12:22:44 | ClassPredicate bar | -| Overrides.qll:28:3:28:9 | MemberCall | Overrides.qll:6:3:6:29 | ClassPredicate bar | -| Overrides.qll:29:3:29:10 | MemberCall | Overrides.qll:8:3:8:41 | ClassPredicate baz | -| ParamModules.qll:5:28:5:41 | PredicateCall | ParamModules.qll:2:13:2:36 | ClasslessPredicate fooSig | -| ParamModules.qll:5:28:5:41 | PredicateCall | ParamModules.qll:8:3:8:35 | ClasslessPredicate myFoo | -| ParamModules.qll:10:26:10:49 | PredicateCall | ParamModules.qll:5:5:5:43 | ClasslessPredicate bar | -| ParamModules.qll:26:27:26:53 | PredicateCall | ParamModules.qll:17:5:17:42 | ClasslessPredicate getAnEven | -| ParamModules.qll:26:27:26:61 | MemberCall | ParamModules.qll:23:5:23:39 | ClassPredicate myFoo | -| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:33:5:33:17 | ClasslessPredicate getThing | -| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:51:5:51:26 | ClasslessPredicate getThing | -| ParamModules.qll:59:30:59:45 | PredicateCall | ParamModules.qll:37:5:37:49 | ClasslessPredicate getThing | -| ParamModules.qll:59:30:59:53 | MemberCall | ParamModules.qll:46:7:46:41 | ClassPredicate myFoo | -| ParamModules.qll:61:39:61:47 | MemberCall | ParamModules.qll:46:7:46:41 | ClassPredicate myFoo | -| packs/other/OtherThing.qll:5:3:5:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:1:1:30 | ClasslessPredicate foo | -| packs/other/OtherThing.qll:6:3:6:8 | PredicateCall | packs/src/SrcThing.qll:8:1:8:30 | ClasslessPredicate bar | -| packs/src/SrcThing.qll:4:3:4:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:1:1:30 | ClasslessPredicate foo | -| packs/src/SrcThing.qll:5:3:5:8 | PredicateCall | packs/src/SrcThing.qll:8:1:8:30 | ClasslessPredicate bar | +| MultiResolve.qll:14:25:14:35 | PredicateCall | MultiResolve.qll:1:11:1:13 | ClasslessPredicate foo | +| MultiResolve.qll:14:25:14:35 | PredicateCall | MultiResolve.qll:12:11:12:15 | ClasslessPredicate myFoo | +| MultiResolve.qll:23:7:23:18 | PredicateCall | MultiResolve.qll:17:13:17:15 | ClasslessPredicate bar | +| MultiResolve.qll:41:30:41:47 | MemberCall | MultiResolve.qll:31:13:31:15 | ClassPredicate foo | +| MultiResolve.qll:49:3:49:12 | PredicateCall | MultiResolve.qll:45:13:45:15 | ClasslessPredicate foo | +| MultiResolve.qll:53:30:53:47 | MemberCall | MultiResolve.qll:37:13:37:15 | ClassPredicate foo | +| MultiResolve.qll:56:5:56:14 | MemberCall | MultiResolve.qll:53:22:53:24 | ClassPredicate foo | +| Overrides.qll:8:30:8:39 | MemberCall | Overrides.qll:6:7:6:9 | ClassPredicate bar | +| Overrides.qll:16:39:16:48 | MemberCall | Overrides.qll:14:16:14:18 | ClassPredicate bar | +| Overrides.qll:24:39:24:48 | MemberCall | Overrides.qll:22:16:22:18 | ClassPredicate bar | +| Overrides.qll:28:3:28:9 | MemberCall | Overrides.qll:6:7:6:9 | ClassPredicate bar | +| Overrides.qll:29:3:29:10 | MemberCall | Overrides.qll:8:13:8:15 | ClassPredicate baz | +| ParamModules.qll:5:28:5:41 | PredicateCall | ParamModules.qll:2:23:2:28 | ClasslessPredicate fooSig | +| ParamModules.qll:5:28:5:41 | PredicateCall | ParamModules.qll:8:13:8:17 | ClasslessPredicate myFoo | +| ParamModules.qll:10:26:10:49 | PredicateCall | ParamModules.qll:5:15:5:17 | ClasslessPredicate bar | +| ParamModules.qll:26:27:26:53 | PredicateCall | ParamModules.qll:17:13:17:21 | ClasslessPredicate getAnEven | +| ParamModules.qll:26:27:26:61 | MemberCall | ParamModules.qll:23:12:23:16 | ClassPredicate myFoo | +| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:33:7:33:14 | ClasslessPredicate getThing | +| ParamModules.qll:37:29:37:47 | PredicateCall | ParamModules.qll:51:7:51:14 | ClasslessPredicate getThing | +| ParamModules.qll:59:30:59:45 | PredicateCall | ParamModules.qll:37:7:37:14 | ClasslessPredicate getThing | +| ParamModules.qll:59:30:59:53 | MemberCall | ParamModules.qll:46:14:46:18 | ClassPredicate myFoo | +| ParamModules.qll:61:39:61:47 | MemberCall | ParamModules.qll:46:14:46:18 | ClassPredicate myFoo | +| packs/other/OtherThing.qll:5:3:5:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:11:1:13 | ClasslessPredicate foo | +| packs/other/OtherThing.qll:6:3:6:8 | PredicateCall | packs/src/SrcThing.qll:8:11:8:13 | ClasslessPredicate bar | +| packs/src/SrcThing.qll:4:3:4:8 | PredicateCall | packs/lib/LibThing/Foo.qll:1:11:1:13 | ClasslessPredicate foo | +| packs/src/SrcThing.qll:5:3:5:8 | PredicateCall | packs/src/SrcThing.qll:8:11:8:13 | ClasslessPredicate bar | dependsOn | packs/other/qlpack.yml:1:1:1:4 | ql-other-pack-thing | packs/src/qlpack.yml:1:1:1:4 | ql-testing-src-pack | | packs/src/qlpack.yml:1:1:1:4 | ql-testing-src-pack | packs/lib/qlpack.yml:1:1:1:4 | ql-testing-lib-pack | exprPredicate -| Foo.qll:24:22:24:31 | predicate | Foo.qll:22:3:22:32 | ClasslessPredicate myThing0 | -| Foo.qll:26:22:26:31 | predicate | Foo.qll:20:3:20:54 | ClasslessPredicate myThing2 | -| Foo.qll:47:55:47:62 | predicate | Foo.qll:42:20:42:27 | NewTypeBranch MkRoot | -| Foo.qll:47:65:47:70 | predicate | Foo.qll:44:9:44:56 | ClasslessPredicate edge | -| ParamModules.qll:4:18:4:25 | predicate | ParamModules.qll:2:13:2:36 | ClasslessPredicate fooSig | -| ParamModules.qll:10:34:10:40 | predicate | ParamModules.qll:8:3:8:35 | ClasslessPredicate myFoo | +| Foo.qll:24:22:24:31 | predicate | Foo.qll:22:13:22:20 | ClasslessPredicate myThing0 | +| Foo.qll:26:22:26:31 | predicate | Foo.qll:20:13:20:20 | ClasslessPredicate myThing2 | +| Foo.qll:47:55:47:62 | predicate | Foo.qll:42:20:42:25 | NewTypeBranch MkRoot | +| Foo.qll:47:65:47:70 | predicate | Foo.qll:44:19:44:22 | ClasslessPredicate edge | +| MultiResolve.qll:12:19:12:23 | predicate | MultiResolve.qll:1:11:1:13 | ClasslessPredicate foo | +| ParamModules.qll:4:18:4:25 | predicate | ParamModules.qll:2:23:2:28 | ClasslessPredicate fooSig | +| ParamModules.qll:10:34:10:40 | predicate | ParamModules.qll:8:13:8:17 | ClasslessPredicate myFoo | diff --git a/ql/ql/test/printAst/printAst.expected b/ql/ql/test/printAst/printAst.expected index 603608355d7..daa299215d3 100644 --- a/ql/ql/test/printAst/printAst.expected +++ b/ql/ql/test/printAst/printAst.expected @@ -3,8 +3,8 @@ nodes | Foo.qll:1:1:1:17 | Import | semmle.order | 1 | | Foo.qll:1:1:27:2 | TopLevel | semmle.label | [TopLevel] TopLevel | | Foo.qll:1:1:27:2 | TopLevel | semmle.order | 1 | -| Foo.qll:3:1:7:1 | Class Foo | semmle.label | [Class] Class Foo | -| Foo.qll:3:1:7:1 | Class Foo | semmle.order | 3 | +| Foo.qll:3:7:3:9 | Class Foo | semmle.label | [Class] Class Foo | +| Foo.qll:3:7:3:9 | Class Foo | semmle.order | 3 | | Foo.qll:3:19:3:22 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | | Foo.qll:3:19:3:22 | TypeExpr | semmle.order | 4 | | Foo.qll:4:3:4:17 | CharPred Foo | semmle.label | [CharPred] CharPred Foo | @@ -17,8 +17,8 @@ nodes | Foo.qll:4:15:4:15 | Integer | semmle.order | 8 | | Foo.qll:6:3:6:8 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | | Foo.qll:6:3:6:8 | TypeExpr | semmle.order | 9 | -| Foo.qll:6:3:6:38 | ClassPredicate toString | semmle.label | [ClassPredicate] ClassPredicate toString | -| Foo.qll:6:3:6:38 | ClassPredicate toString | semmle.order | 9 | +| Foo.qll:6:10:6:17 | ClassPredicate toString | semmle.label | [ClassPredicate] ClassPredicate toString | +| Foo.qll:6:10:6:17 | ClassPredicate toString | semmle.order | 10 | | Foo.qll:6:23:6:28 | result | semmle.label | [ResultAccess] result | | Foo.qll:6:23:6:28 | result | semmle.order | 11 | | Foo.qll:6:23:6:36 | ComparisonFormula | semmle.label | [ComparisonFormula] ComparisonFormula | @@ -27,8 +27,8 @@ nodes | Foo.qll:6:32:6:36 | String | semmle.order | 13 | | Foo.qll:9:1:9:5 | annotation | semmle.label | [Annotation] annotation | | Foo.qll:9:1:9:5 | annotation | semmle.order | 14 | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | semmle.label | [ClasslessPredicate] ClasslessPredicate foo | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | semmle.order | 15 | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.label | [ClasslessPredicate] ClasslessPredicate foo | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.order | 15 | | Foo.qll:9:21:9:23 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | | Foo.qll:9:21:9:23 | TypeExpr | semmle.order | 16 | | Foo.qll:9:21:9:25 | f | semmle.label | [VarDecl] f | @@ -59,8 +59,8 @@ nodes | Foo.qll:10:69:10:73 | inner | semmle.order | 29 | | Foo.qll:10:69:10:84 | MemberCall | semmle.label | [MemberCall] MemberCall | | Foo.qll:10:69:10:84 | MemberCall | semmle.order | 29 | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | semmle.label | [ClasslessPredicate] ClasslessPredicate calls | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | semmle.order | 31 | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.label | [ClasslessPredicate] ClasslessPredicate calls | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.order | 31 | | Foo.qll:13:17:13:19 | TypeExpr | semmle.label | [TypeExpr] TypeExpr | | Foo.qll:13:17:13:19 | TypeExpr | semmle.order | 32 | | Foo.qll:13:17:13:21 | f | semmle.label | [VarDecl] f | @@ -239,38 +239,38 @@ nodes edges | Foo.qll:1:1:27:2 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.label | getAnImport() | | Foo.qll:1:1:27:2 | TopLevel | Foo.qll:1:1:1:17 | Import | semmle.order | 1 | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:3:1:7:1 | Class Foo | semmle.label | getAClass() | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:3:1:7:1 | Class Foo | semmle.order | 3 | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:9:7:11:1 | ClasslessPredicate foo | semmle.label | getAPredicate() | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:9:7:11:1 | ClasslessPredicate foo | semmle.order | 15 | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:13:1:27:1 | ClasslessPredicate calls | semmle.label | getAPredicate() | -| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:13:1:27:1 | ClasslessPredicate calls | semmle.order | 31 | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.label | getASuperType() | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.order | 4 | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:4:3:4:17 | CharPred Foo | semmle.label | getCharPred() | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:4:3:4:17 | CharPred Foo | semmle.order | 5 | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:6:3:6:38 | ClassPredicate toString | semmle.label | getClassPredicate(_) | -| Foo.qll:3:1:7:1 | Class Foo | Foo.qll:6:3:6:38 | ClassPredicate toString | semmle.order | 9 | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.label | getAClass() | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:3:7:3:9 | Class Foo | semmle.order | 3 | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.label | getAPredicate() | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:9:17:9:19 | ClasslessPredicate foo | semmle.order | 15 | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.label | getAPredicate() | +| Foo.qll:1:1:27:2 | TopLevel | Foo.qll:13:11:13:15 | ClasslessPredicate calls | semmle.order | 31 | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.label | getASuperType() | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:3:19:3:22 | TypeExpr | semmle.order | 4 | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:4:3:4:17 | CharPred Foo | semmle.label | getCharPred() | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:4:3:4:17 | CharPred Foo | semmle.order | 5 | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:6:10:6:17 | ClassPredicate toString | semmle.label | getClassPredicate(_) | +| Foo.qll:3:7:3:9 | Class Foo | Foo.qll:6:10:6:17 | ClassPredicate toString | semmle.order | 10 | | Foo.qll:4:3:4:17 | CharPred Foo | Foo.qll:4:11:4:15 | ComparisonFormula | semmle.label | getBody() | | Foo.qll:4:3:4:17 | CharPred Foo | Foo.qll:4:11:4:15 | ComparisonFormula | semmle.order | 6 | | Foo.qll:4:11:4:15 | ComparisonFormula | Foo.qll:4:11:4:11 | Integer | semmle.label | getLeftOperand() | | Foo.qll:4:11:4:15 | ComparisonFormula | Foo.qll:4:11:4:11 | Integer | semmle.order | 6 | | Foo.qll:4:11:4:15 | ComparisonFormula | Foo.qll:4:15:4:15 | Integer | semmle.label | getRightOperand() | | Foo.qll:4:11:4:15 | ComparisonFormula | Foo.qll:4:15:4:15 | Integer | semmle.order | 8 | -| Foo.qll:6:3:6:38 | ClassPredicate toString | Foo.qll:6:3:6:8 | TypeExpr | semmle.label | getReturnTypeExpr() | -| Foo.qll:6:3:6:38 | ClassPredicate toString | Foo.qll:6:3:6:8 | TypeExpr | semmle.order | 9 | -| Foo.qll:6:3:6:38 | ClassPredicate toString | Foo.qll:6:23:6:36 | ComparisonFormula | semmle.label | getBody() | -| Foo.qll:6:3:6:38 | ClassPredicate toString | Foo.qll:6:23:6:36 | ComparisonFormula | semmle.order | 11 | +| Foo.qll:6:10:6:17 | ClassPredicate toString | Foo.qll:6:3:6:8 | TypeExpr | semmle.label | getReturnTypeExpr() | +| Foo.qll:6:10:6:17 | ClassPredicate toString | Foo.qll:6:3:6:8 | TypeExpr | semmle.order | 9 | +| Foo.qll:6:10:6:17 | ClassPredicate toString | Foo.qll:6:23:6:36 | ComparisonFormula | semmle.label | getBody() | +| Foo.qll:6:10:6:17 | ClassPredicate toString | Foo.qll:6:23:6:36 | ComparisonFormula | semmle.order | 11 | | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:23:6:28 | result | semmle.label | getLeftOperand() | | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:23:6:28 | result | semmle.order | 11 | | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:32:6:36 | String | semmle.label | getRightOperand() | | Foo.qll:6:23:6:36 | ComparisonFormula | Foo.qll:6:32:6:36 | String | semmle.order | 13 | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:1:9:5 | annotation | semmle.label | getAnAnnotation() | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:1:9:5 | annotation | semmle.order | 14 | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:21:9:25 | f | semmle.label | getParameter(_) | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:9:21:9:25 | f | semmle.order | 16 | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:10:3:10:85 | ComparisonFormula | semmle.label | getBody() | -| Foo.qll:9:7:11:1 | ClasslessPredicate foo | Foo.qll:10:3:10:85 | ComparisonFormula | semmle.order | 18 | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:9:1:9:5 | annotation | semmle.label | getAnAnnotation() | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:9:1:9:5 | annotation | semmle.order | 14 | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:9:21:9:25 | f | semmle.label | getParameter(_) | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:9:21:9:25 | f | semmle.order | 16 | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:10:3:10:85 | ComparisonFormula | semmle.label | getBody() | +| Foo.qll:9:17:9:19 | ClasslessPredicate foo | Foo.qll:10:3:10:85 | ComparisonFormula | semmle.order | 18 | | Foo.qll:9:21:9:25 | f | Foo.qll:9:21:9:23 | TypeExpr | semmle.label | getTypeExpr() | | Foo.qll:9:21:9:25 | f | Foo.qll:9:21:9:23 | TypeExpr | semmle.order | 16 | | Foo.qll:10:3:10:85 | ComparisonFormula | Foo.qll:10:3:10:3 | f | semmle.label | getLeftOperand() | @@ -297,10 +297,10 @@ edges | Foo.qll:10:27:10:50 | ComparisonFormula | Foo.qll:10:46:10:50 | String | semmle.order | 27 | | Foo.qll:10:69:10:84 | MemberCall | Foo.qll:10:69:10:73 | inner | semmle.label | getBase() | | Foo.qll:10:69:10:84 | MemberCall | Foo.qll:10:69:10:73 | inner | semmle.order | 29 | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | Foo.qll:13:17:13:21 | f | semmle.label | getParameter(_) | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | Foo.qll:13:17:13:21 | f | semmle.order | 32 | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | Foo.qll:14:3:26:14 | Disjunction | semmle.label | getBody() | -| Foo.qll:13:1:27:1 | ClasslessPredicate calls | Foo.qll:14:3:26:14 | Disjunction | semmle.order | 34 | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | Foo.qll:13:17:13:21 | f | semmle.label | getParameter(_) | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | Foo.qll:13:17:13:21 | f | semmle.order | 32 | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | Foo.qll:14:3:26:14 | Disjunction | semmle.label | getBody() | +| Foo.qll:13:11:13:15 | ClasslessPredicate calls | Foo.qll:14:3:26:14 | Disjunction | semmle.order | 34 | | Foo.qll:13:17:13:21 | f | Foo.qll:13:17:13:19 | TypeExpr | semmle.label | getTypeExpr() | | Foo.qll:13:17:13:21 | f | Foo.qll:13:17:13:19 | TypeExpr | semmle.order | 32 | | Foo.qll:14:3:14:10 | PredicateCall | Foo.qll:14:9:14:9 | f | semmle.label | getArgument(_) | diff --git a/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.expected b/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.expected index bd6bc99489d..de23b92bafa 100644 --- a/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.expected +++ b/ql/ql/test/queries/style/AcronymsShouldBeCamelCase/AcronymsShouldBeCamelCase.expected @@ -1,3 +1,3 @@ -| Test.qll:2:1:2:27 | ClasslessPredicate isXML | Acronyms in isXML should be PascalCase/camelCase | -| Test.qll:8:1:10:15 | NewType TXMLElements | Acronyms in TXMLElements should be PascalCase/camelCase | -| Test.qll:10:3:10:15 | NewTypeBranch TXMLElement | Acronyms in TXMLElement should be PascalCase/camelCase | +| Test.qll:2:11:2:15 | ClasslessPredicate isXML | Acronyms in isXML should be PascalCase/camelCase | +| Test.qll:8:9:8:20 | NewType TXMLElements | Acronyms in TXMLElements should be PascalCase/camelCase | +| Test.qll:10:3:10:13 | NewTypeBranch TXMLElement | Acronyms in TXMLElement should be PascalCase/camelCase | diff --git a/ql/ql/test/queries/style/DeadCode/DeadCode.expected b/ql/ql/test/queries/style/DeadCode/DeadCode.expected index d3b3115e729..39a2b034390 100644 --- a/ql/ql/test/queries/style/DeadCode/DeadCode.expected +++ b/ql/ql/test/queries/style/DeadCode/DeadCode.expected @@ -1,2 +1,2 @@ -| Foo.qll:2:11:2:38 | ClasslessPredicate dead1 | Code is dead | -| Foo.qll:6:3:6:30 | ClasslessPredicate dead2 | Code is dead | +| Foo.qll:2:21:2:25 | ClasslessPredicate dead1 | Code is dead | +| Foo.qll:6:13:6:17 | ClasslessPredicate dead2 | Code is dead | diff --git a/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected b/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected index f59b21d0d73..2ef0dd4b1ad 100644 --- a/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected +++ b/ql/ql/test/queries/style/MissingOverride/MissingOverride.expected @@ -1 +1 @@ -| Test.qll:12:3:12:33 | ClassPredicate test | Wrong.test overrides $@ but does not have an override annotation. | Test.qll:4:3:4:40 | ClassPredicate test | Super.test | +| Test.qll:12:13:12:16 | ClassPredicate test | Wrong.test overrides $@ but does not have an override annotation. | Test.qll:4:13:4:16 | ClassPredicate test | Super.test | diff --git a/ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll b/ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll new file mode 100644 index 00000000000..13509dbe521 --- /dev/null +++ b/ql/ql/test/queries/style/MissingParameterInQlDoc/Foo.qll @@ -0,0 +1,14 @@ +/** `param1`, `param2`, and `param3` are the parameters. */ +predicate test1(int param1, int param2, int param3) { none() } // OK + +/** `param1`, `par2` */ +predicate test2(int param1, int param2) { none() } // NOT OK - `par2` is not a parameter, and `param2` has no documentation + +/** `param1`, `par2 + par3` */ +predicate test3(int param1, int par2, int par3) { none() } // OK + +/** this mentions no parameters */ +predicate test4(int param1, int param2) { none() } // OK - the QLDoc mentions none of the parameters, that's OK + +/** the param1 parameter is mentioned in a non-code block, but the `par2` parameter is misspelled */ +predicate test5(int param1, int param2) { none() } // NOT OK - the `param1` parameter is "documented" in clear text, but `par2` is misspelled diff --git a/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected new file mode 100644 index 00000000000..4307178da72 --- /dev/null +++ b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.expected @@ -0,0 +1,2 @@ +| Foo.qll:5:11:5:15 | ClasslessPredicate test2 | The QLDoc has no documentation for param2, but the QLDoc mentions par2 | +| Foo.qll:14:11:14:15 | ClasslessPredicate test5 | The QLDoc has no documentation for param2, but the QLDoc mentions par2 | diff --git a/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref new file mode 100644 index 00000000000..0539e4f5de2 --- /dev/null +++ b/ql/ql/test/queries/style/MissingParameterInQlDoc/MissingParameterInQlDoc.qlref @@ -0,0 +1 @@ +queries/style/MissingParameterInQlDoc.ql \ No newline at end of file diff --git a/ql/ql/test/queries/style/Misspelling/Misspelling.expected b/ql/ql/test/queries/style/Misspelling/Misspelling.expected index dc028c412d8..65f9f245d3b 100644 --- a/ql/ql/test/queries/style/Misspelling/Misspelling.expected +++ b/ql/ql/test/queries/style/Misspelling/Misspelling.expected @@ -1,6 +1,6 @@ | Test.qll:1:1:3:3 | QLDoc | This QLDoc comment contains the common misspelling 'mispelled', which should instead be 'misspelled'. | -| Test.qll:4:1:11:1 | Class PublicallyAccessible | This class name contains the common misspelling 'publically', which should instead be 'publicly'. | +| Test.qll:4:7:4:26 | Class PublicallyAccessible | This class name contains the common misspelling 'publically', which should instead be 'publicly'. | | Test.qll:5:3:5:20 | FieldDecl | This field name contains the common misspelling 'occurences', which should instead be 'occurrences'. | -| Test.qll:10:3:10:36 | ClassPredicate hasAgrument | This classPredicate name contains the common misspelling 'agrument', which should instead be 'argument'. | +| Test.qll:10:13:10:23 | ClassPredicate hasAgrument | This classPredicate name contains the common misspelling 'agrument', which should instead be 'argument'. | | Test.qll:13:1:16:3 | QLDoc | This QLDoc comment contains the non-US spelling 'colour', which should instead be 'color'. | -| Test.qll:17:1:22:1 | Class AnalysedInt | This class name contains the non-US spelling 'analysed', which should instead be 'analyzed'. | +| Test.qll:17:7:17:17 | Class AnalysedInt | This class name contains the non-US spelling 'analysed', which should instead be 'analyzed'. | diff --git a/ql/ql/test/queries/style/RedundantCast/Foo.qll b/ql/ql/test/queries/style/RedundantCast/Foo.qll new file mode 100644 index 00000000000..d993f654bc4 --- /dev/null +++ b/ql/ql/test/queries/style/RedundantCast/Foo.qll @@ -0,0 +1,11 @@ +class Foo extends string { + Foo() { this = "Foo" } +} + +predicate test(Foo f) { f.(Foo).toString() = "X" } + +predicate test2(Foo a, Foo b) { a.(Foo) = b } + +predicate called(Foo a) { a.toString() = "X" } + +predicate test3(string s) { called(s.(Foo)) } diff --git a/ql/ql/test/queries/style/RedundantCast/RedundantCast.expected b/ql/ql/test/queries/style/RedundantCast/RedundantCast.expected new file mode 100644 index 00000000000..e4e57083633 --- /dev/null +++ b/ql/ql/test/queries/style/RedundantCast/RedundantCast.expected @@ -0,0 +1,3 @@ +| Foo.qll:5:25:5:31 | InlineCast | Redundant cast to $@ | Foo.qll:5:28:5:30 | TypeExpr | Foo | +| Foo.qll:7:33:7:39 | InlineCast | Redundant cast to $@ | Foo.qll:7:36:7:38 | TypeExpr | Foo | +| Foo.qll:11:36:11:42 | InlineCast | Redundant cast to $@ | Foo.qll:11:39:11:41 | TypeExpr | Foo | diff --git a/ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref b/ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref new file mode 100644 index 00000000000..659062d3ae5 --- /dev/null +++ b/ql/ql/test/queries/style/RedundantCast/RedundantCast.qlref @@ -0,0 +1 @@ +queries/style/RedundantCast.ql diff --git a/ql/ql/test/type/type.expected b/ql/ql/test/type/type.expected index 773a9244a22..fd3a34c27f6 100644 --- a/ql/ql/test/type/type.expected +++ b/ql/ql/test/type/type.expected @@ -1,6 +1,6 @@ -| Test.qll:4:15:4:18 | this | Test.qll:3:1:5:1 | Strings | -| Test.qll:4:15:4:18 | this | Test.qll:3:1:5:1 | Strings.Strings | -| Test.qll:4:15:4:18 | this | Test.qll:3:1:5:1 | Strings.extends | +| Test.qll:4:15:4:18 | this | Test.qll:3:7:3:13 | Strings | +| Test.qll:4:15:4:18 | this | Test.qll:3:7:3:13 | Strings.Strings | +| Test.qll:4:15:4:18 | this | Test.qll:3:7:3:13 | Strings.extends | | Test.qll:4:22:4:76 | Set | file://:0:0:0:0 | string | | Test.qll:4:23:4:24 | String | file://:0:0:0:0 | string | | Test.qll:4:27:4:29 | String | file://:0:0:0:0 | string | @@ -12,9 +12,9 @@ | Test.qll:4:61:4:63 | String | file://:0:0:0:0 | string | | Test.qll:4:66:4:69 | String | file://:0:0:0:0 | string | | Test.qll:4:72:4:75 | String | file://:0:0:0:0 | string | -| Test.qll:8:14:8:17 | this | Test.qll:7:1:9:1 | Floats | -| Test.qll:8:14:8:17 | this | Test.qll:7:1:9:1 | Floats.Floats | -| Test.qll:8:14:8:17 | this | Test.qll:7:1:9:1 | Floats.extends | +| Test.qll:8:14:8:17 | this | Test.qll:7:7:7:12 | Floats | +| Test.qll:8:14:8:17 | this | Test.qll:7:7:7:12 | Floats.Floats | +| Test.qll:8:14:8:17 | this | Test.qll:7:7:7:12 | Floats.extends | | Test.qll:8:21:8:70 | Set | file://:0:0:0:0 | float | | Test.qll:8:22:8:24 | Float | file://:0:0:0:0 | float | | Test.qll:8:27:8:29 | Float | file://:0:0:0:0 | float | @@ -27,28 +27,28 @@ | Test.qll:8:62:8:64 | Float | file://:0:0:0:0 | float | | Test.qll:8:67:8:69 | Float | file://:0:0:0:0 | float | | Test.qll:11:37:11:42 | result | file://:0:0:0:0 | string | -| Test.qll:11:46:11:46 | a | Test.qll:3:1:5:1 | Strings | +| Test.qll:11:46:11:46 | a | Test.qll:3:7:3:13 | Strings | | Test.qll:11:46:11:50 | AddExpr | file://:0:0:0:0 | string | -| Test.qll:11:50:11:50 | b | Test.qll:3:1:5:1 | Strings | +| Test.qll:11:50:11:50 | b | Test.qll:3:7:3:13 | Strings | | Test.qll:13:36:13:41 | result | file://:0:0:0:0 | float | -| Test.qll:13:45:13:45 | a | Test.qll:7:1:9:1 | Floats | +| Test.qll:13:45:13:45 | a | Test.qll:7:7:7:12 | Floats | | Test.qll:13:45:13:49 | AddExpr | file://:0:0:0:0 | float | -| Test.qll:13:49:13:49 | b | Test.qll:7:1:9:1 | Floats | -| Test.qll:16:12:16:15 | this | Test.qll:15:1:19:1 | Base | -| Test.qll:16:12:16:15 | this | Test.qll:15:1:19:1 | Base.Base | -| Test.qll:16:12:16:15 | this | Test.qll:15:1:19:1 | Base.extends | +| Test.qll:13:49:13:49 | b | Test.qll:7:7:7:12 | Floats | +| Test.qll:16:12:16:15 | this | Test.qll:15:7:15:10 | Base | +| Test.qll:16:12:16:15 | this | Test.qll:15:7:15:10 | Base.Base | +| Test.qll:16:12:16:15 | this | Test.qll:15:7:15:10 | Base.extends | | Test.qll:16:19:16:23 | String | file://:0:0:0:0 | string | | Test.qll:18:15:18:20 | result | file://:0:0:0:0 | int | | Test.qll:18:24:18:24 | Integer | file://:0:0:0:0 | int | -| Test.qll:22:11:22:14 | this | Test.qll:21:1:27:1 | Sub | -| Test.qll:22:11:22:14 | this | Test.qll:21:1:27:1 | Sub.Sub | -| Test.qll:22:11:22:14 | this | Test.qll:21:1:27:1 | Sub.extends | +| Test.qll:22:11:22:14 | this | Test.qll:21:7:21:9 | Sub | +| Test.qll:22:11:22:14 | this | Test.qll:21:7:21:9 | Sub.Sub | +| Test.qll:22:11:22:14 | this | Test.qll:21:7:21:9 | Sub.extends | | Test.qll:22:18:22:22 | String | file://:0:0:0:0 | string | | Test.qll:24:15:24:20 | result | file://:0:0:0:0 | int | -| Test.qll:24:24:24:33 | Super | Test.qll:15:1:19:1 | Base | +| Test.qll:24:24:24:33 | Super | Test.qll:15:7:15:10 | Base | | Test.qll:24:24:24:39 | MemberCall | file://:0:0:0:0 | int | | Test.qll:26:16:26:21 | result | file://:0:0:0:0 | int | -| Test.qll:26:25:26:29 | Super | Test.qll:15:1:19:1 | Base | +| Test.qll:26:25:26:29 | Super | Test.qll:15:7:15:10 | Base | | Test.qll:26:25:26:35 | MemberCall | file://:0:0:0:0 | int | | Test.qll:30:32:30:37 | result | file://:0:0:0:0 | int | | Test.qll:30:41:30:41 | a | file://:0:0:0:0 | int | diff --git a/ql/create-extractor-pack.ps1 b/ql/scripts/create-extractor-pack.ps1 similarity index 100% rename from ql/create-extractor-pack.ps1 rename to ql/scripts/create-extractor-pack.ps1 diff --git a/ql/create-extractor-pack.sh b/ql/scripts/create-extractor-pack.sh similarity index 100% rename from ql/create-extractor-pack.sh rename to ql/scripts/create-extractor-pack.sh diff --git a/ql/scripts/merge-sarif.js b/ql/scripts/merge-sarif.js new file mode 100644 index 00000000000..5c781f1b67d --- /dev/null +++ b/ql/scripts/merge-sarif.js @@ -0,0 +1,23 @@ +var fs = require("fs"); + +// first a list of files to merge, and the last argument is the output file. +async function main(files) { + const inputs = files + .slice(0, -1) + .map((file) => fs.readFileSync(file)) + .map((data) => JSON.parse(data)); + const out = inputs[0]; // just arbitrarily take the first one + const outFile = files[files.length - 1]; + + const combinedResults = []; + + for (const sarif of inputs) { + combinedResults.push(...sarif.runs[0].results); + } + + out.runs[0].artifacts = []; // the indexes in these won't make sense, so I hope this works. + out.runs[0].results = combinedResults; + + fs.writeFileSync(outFile, JSON.stringify(out, null, 2)); +} +main(process.argv.splice(2)); diff --git a/ql/scripts/split-sarif.js b/ql/scripts/split-sarif.js new file mode 100644 index 00000000000..d09989abf8a --- /dev/null +++ b/ql/scripts/split-sarif.js @@ -0,0 +1,30 @@ +var fs = require("fs"); + +// the .sarif file to split, and then the directory to put the split files in. +async function main(inputs) { + const sarifFile = JSON.parse(fs.readFileSync(inputs[0])); + const outFolder = inputs[1]; + + const out = {}; + + for (const result of sarifFile.runs[0].results) { + const lang = getLanguage(result); + if (!out[lang]) { + out[lang] = []; + } + out[lang].push(result); + } + + for (const lang in out) { + const outSarif = JSON.parse(JSON.stringify(sarifFile)); + outSarif.runs[0].results = out[lang]; + fs.writeFileSync(`${outFolder}/${lang}.sarif`, JSON.stringify(outSarif, null, 2)); + } +} + +function getLanguage(result) { + return result.locations[0].physicalLocation.artifactLocation.uri.split( + "/" + )[0]; +} +main(process.argv.splice(2)); diff --git a/ruby/Cargo.lock b/ruby/Cargo.lock index 1be66824a3e..2e2f205ce02 100644 Binary files a/ruby/Cargo.lock and b/ruby/Cargo.lock differ diff --git a/ruby/autobuilder/src/main.rs b/ruby/autobuilder/src/main.rs index 18892ae2d5c..ae4aa816190 100644 --- a/ruby/autobuilder/src/main.rs +++ b/ruby/autobuilder/src/main.rs @@ -19,6 +19,7 @@ fn main() -> std::io::Result<()> { .arg("--include-extension=.erb") .arg("--include-extension=.gemspec") .arg("--include=**/Gemfile") + .arg("--exclude=**/.git") .arg("--size-limit=5m") .arg("--language=ruby") .arg("--working-dir=.") @@ -29,9 +30,9 @@ fn main() -> std::io::Result<()> { .split('\n') { if let Some(stripped) = line.strip_prefix("include:") { - cmd.arg("--include").arg(stripped); + cmd.arg("--also-match=".to_owned() + stripped); } else if let Some(stripped) = line.strip_prefix("exclude:") { - cmd.arg("--exclude").arg(stripped); + cmd.arg("--exclude=".to_owned() + stripped); } } let exit = &cmd.spawn()?.wait()?; diff --git a/ruby/extractor/Cargo.toml b/ruby/extractor/Cargo.toml index 14dd2a4e78c..c1a7c4424b9 100644 --- a/ruby/extractor/Cargo.toml +++ b/ruby/extractor/Cargo.toml @@ -11,10 +11,12 @@ flate2 = "1.0" node-types = { path = "../node-types" } tree-sitter = "0.19" tree-sitter-embedded-template = { git = "https://github.com/tree-sitter/tree-sitter-embedded-template.git", rev = "1a538da253d73f896b9f6c0c7d79cda58791ac5c" } -tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "5b305c3cd32db10494cedd2743de6bbe32f1a573" } +tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "e75d04404c9dd71ad68850d5c672b226d5e694f3" } clap = "3.0" tracing = "0.1" tracing-subscriber = { version = "0.3.3", features = ["env-filter"] } rayon = "1.5.0" num_cpus = "1.13.0" regex = "1.5.5" +encoding = "0.2" +lazy_static = "1.4.0" diff --git a/ruby/extractor/src/extractor.rs b/ruby/extractor/src/extractor.rs index 8cdaff1b738..db280634ae5 100644 --- a/ruby/extractor/src/extractor.rs +++ b/ruby/extractor/src/extractor.rs @@ -1,161 +1,112 @@ +use crate::trap; use node_types::{EntryKind, Field, NodeTypeMap, Storage, TypeName}; -use std::borrow::Cow; use std::collections::BTreeMap as Map; use std::collections::BTreeSet as Set; use std::fmt; -use std::io::Write; use std::path::Path; use tracing::{error, info, span, Level}; use tree_sitter::{Language, Node, Parser, Range, Tree}; -pub struct TrapWriter { - /// The accumulated trap entries - trap_output: Vec, - /// A counter for generating fresh labels - counter: u32, - /// cache of global keys - global_keys: std::collections::HashMap, +pub fn populate_file(writer: &mut trap::Writer, absolute_path: &Path) -> trap::Label { + let (file_label, fresh) = + writer.global_id(&trap::full_id_for_file(&normalize_path(absolute_path))); + if fresh { + writer.add_tuple( + "files", + vec![ + trap::Arg::Label(file_label), + trap::Arg::String(normalize_path(absolute_path)), + ], + ); + populate_parent_folders(writer, file_label, absolute_path.parent()); + } + file_label } -pub fn new_trap_writer() -> TrapWriter { - TrapWriter { - counter: 0, - trap_output: Vec::new(), - global_keys: std::collections::HashMap::new(), +fn populate_empty_file(writer: &mut trap::Writer) -> trap::Label { + let (file_label, fresh) = writer.global_id("empty;sourcefile"); + if fresh { + writer.add_tuple( + "files", + vec![ + trap::Arg::Label(file_label), + trap::Arg::String("".to_string()), + ], + ); } + file_label } -impl TrapWriter { - /// Gets a label that will hold the unique ID of the passed string at import time. - /// This can be used for incrementally importable TRAP files -- use globally unique - /// strings to compute a unique ID for table tuples. - /// - /// Note: You probably want to make sure that the key strings that you use are disjoint - /// for disjoint column types; the standard way of doing this is to prefix (or append) - /// the column type name to the ID. Thus, you might identify methods in Java by the - /// full ID "methods_com.method.package.DeclaringClass.method(argumentList)". +pub fn populate_empty_location(writer: &mut trap::Writer) { + let file_label = populate_empty_file(writer); + location(writer, file_label, 0, 0, 0, 0); +} - fn fresh_id(&mut self) -> Label { - let label = Label(self.counter); - self.counter += 1; - self.trap_output.push(TrapEntry::FreshId(label)); - label - } - - fn global_id(&mut self, key: &str) -> (Label, bool) { - if let Some(label) = self.global_keys.get(key) { - return (*label, false); - } - let label = Label(self.counter); - self.counter += 1; - self.global_keys.insert(key.to_owned(), label); - self.trap_output - .push(TrapEntry::MapLabelToKey(label, key.to_owned())); - (label, true) - } - - fn add_tuple(&mut self, table_name: &str, args: Vec) { - self.trap_output - .push(TrapEntry::GenericTuple(table_name.to_owned(), args)) - } - - fn populate_file(&mut self, absolute_path: &Path) -> Label { - let (file_label, fresh) = self.global_id(&full_id_for_file(absolute_path)); - if fresh { - self.add_tuple( - "files", - vec![ - Arg::Label(file_label), - Arg::String(normalize_path(absolute_path)), - ], - ); - self.populate_parent_folders(file_label, absolute_path.parent()); - } - file_label - } - - fn populate_empty_file(&mut self) -> Label { - let (file_label, fresh) = self.global_id("empty;sourcefile"); - if fresh { - self.add_tuple( - "files", - vec![Arg::Label(file_label), Arg::String("".to_string())], - ); - } - file_label - } - - pub fn populate_empty_location(&mut self) { - let file_label = self.populate_empty_file(); - self.location(file_label, 0, 0, 0, 0); - } - - fn populate_parent_folders(&mut self, child_label: Label, path: Option<&Path>) { - let mut path = path; - let mut child_label = child_label; - loop { - match path { - None => break, - Some(folder) => { - let (folder_label, fresh) = self.global_id(&full_id_for_folder(folder)); - self.add_tuple( - "containerparent", - vec![Arg::Label(folder_label), Arg::Label(child_label)], +pub fn populate_parent_folders( + writer: &mut trap::Writer, + child_label: trap::Label, + path: Option<&Path>, +) { + let mut path = path; + let mut child_label = child_label; + loop { + match path { + None => break, + Some(folder) => { + let (folder_label, fresh) = + writer.global_id(&trap::full_id_for_folder(&normalize_path(folder))); + writer.add_tuple( + "containerparent", + vec![ + trap::Arg::Label(folder_label), + trap::Arg::Label(child_label), + ], + ); + if fresh { + writer.add_tuple( + "folders", + vec![ + trap::Arg::Label(folder_label), + trap::Arg::String(normalize_path(folder)), + ], ); - if fresh { - self.add_tuple( - "folders", - vec![ - Arg::Label(folder_label), - Arg::String(normalize_path(folder)), - ], - ); - path = folder.parent(); - child_label = folder_label; - } else { - break; - } + path = folder.parent(); + child_label = folder_label; + } else { + break; } } } } +} - fn location( - &mut self, - file_label: Label, - start_line: usize, - start_column: usize, - end_line: usize, - end_column: usize, - ) -> Label { - let (loc_label, fresh) = self.global_id(&format!( - "loc,{{{}}},{},{},{},{}", - file_label, start_line, start_column, end_line, end_column - )); - if fresh { - self.add_tuple( - "locations_default", - vec![ - Arg::Label(loc_label), - Arg::Label(file_label), - Arg::Int(start_line), - Arg::Int(start_column), - Arg::Int(end_line), - Arg::Int(end_column), - ], - ); - } - loc_label - } - - fn comment(&mut self, text: String) { - self.trap_output.push(TrapEntry::Comment(text)); - } - - pub fn output(self, writer: &mut dyn Write) -> std::io::Result<()> { - write!(writer, "{}", Program(self.trap_output)) +fn location( + writer: &mut trap::Writer, + file_label: trap::Label, + start_line: usize, + start_column: usize, + end_line: usize, + end_column: usize, +) -> trap::Label { + let (loc_label, fresh) = writer.global_id(&format!( + "loc,{{{}}},{},{},{},{}", + file_label, start_line, start_column, end_line, end_column + )); + if fresh { + writer.add_tuple( + "locations_default", + vec![ + trap::Arg::Label(loc_label), + trap::Arg::Label(file_label), + trap::Arg::Int(start_line), + trap::Arg::Int(start_column), + trap::Arg::Int(end_line), + trap::Arg::Int(end_column), + ], + ); } + loc_label } /// Extracts the source file at `path`, which is assumed to be canonicalized. @@ -163,71 +114,43 @@ pub fn extract( language: Language, language_prefix: &str, schema: &NodeTypeMap, - trap_writer: &mut TrapWriter, + trap_writer: &mut trap::Writer, path: &Path, source: &[u8], ranges: &[Range], ) -> std::io::Result<()> { + let path_str = format!("{}", path.display()); let span = span!( Level::TRACE, "extract", - file = %path.display() + file = %path_str ); let _enter = span.enter(); - info!("extracting: {}", path.display()); + info!("extracting: {}", path_str); let mut parser = Parser::new(); parser.set_language(language).unwrap(); parser.set_included_ranges(ranges).unwrap(); let tree = parser.parse(&source, None).expect("Failed to parse file"); - trap_writer.comment(format!("Auto-generated TRAP file for {}", path.display())); - let file_label = &trap_writer.populate_file(path); - let mut visitor = Visitor { + trap_writer.comment(format!("Auto-generated TRAP file for {}", path_str)); + let file_label = populate_file(trap_writer, path); + let mut visitor = Visitor::new( source, trap_writer, // TODO: should we handle path strings that are not valid UTF8 better? - path: format!("{}", path.display()), - file_label: *file_label, - toplevel_child_counter: 0, - stack: Vec::new(), + &path_str, + file_label, language_prefix, schema, - }; + ); traverse(&tree, &mut visitor); parser.reset(); Ok(()) } -/// Escapes a string for use in a TRAP key, by replacing special characters with -/// HTML entities. -fn escape_key<'a, S: Into>>(key: S) -> Cow<'a, str> { - fn needs_escaping(c: char) -> bool { - matches!(c, '&' | '{' | '}' | '"' | '@' | '#') - } - - let key = key.into(); - if key.contains(needs_escaping) { - let mut escaped = String::with_capacity(2 * key.len()); - for c in key.chars() { - match c { - '&' => escaped.push_str("&"), - '{' => escaped.push_str("{"), - '}' => escaped.push_str("}"), - '"' => escaped.push_str("""), - '@' => escaped.push_str("@"), - '#' => escaped.push_str("#"), - _ => escaped.push(c), - } - } - Cow::Owned(escaped) - } else { - key - } -} - /// Normalizes the path according the common CodeQL specification. Assumes that /// `path` has already been canonicalized using `std::fs::canonicalize`. fn normalize_path(path: &Path) -> String { @@ -267,34 +190,28 @@ fn normalize_path(path: &Path) -> String { } } -fn full_id_for_file(path: &Path) -> String { - format!("{};sourcefile", escape_key(&normalize_path(path))) -} - -fn full_id_for_folder(path: &Path) -> String { - format!("{};folder", escape_key(&normalize_path(path))) -} - struct ChildNode { field_name: Option<&'static str>, - label: Label, + label: trap::Label, type_name: TypeName, } struct Visitor<'a> { /// The file path of the source code (as string) - path: String, + path: &'a str, /// The label to use whenever we need to refer to the `@file` entity of this /// source file. - file_label: Label, + file_label: trap::Label, /// The source code as a UTF-8 byte array source: &'a [u8], - /// A TrapWriter to accumulate trap entries - trap_writer: &'a mut TrapWriter, + /// A trap::Writer to accumulate trap entries + trap_writer: &'a mut trap::Writer, /// A counter for top-level child nodes toplevel_child_counter: usize, - /// Language prefix - language_prefix: &'a str, + /// Language-specific name of the AST info table + ast_node_info_table_name: String, + /// Language-specific name of the tokeninfo table + tokeninfo_table_name: String, /// A lookup table from type name to node types schema: &'a NodeTypeMap, /// A stack for gathering information from child nodes. Whenever a node is @@ -303,27 +220,48 @@ struct Visitor<'a> { /// node the list containing the child data is popped from the stack and /// matched against the dbscheme for the node. If the expectations are met /// the corresponding row definitions are added to the trap_output. - stack: Vec<(Label, usize, Vec)>, + stack: Vec<(trap::Label, usize, Vec)>, } -impl Visitor<'_> { +impl<'a> Visitor<'a> { + fn new( + source: &'a [u8], + trap_writer: &'a mut trap::Writer, + path: &'a str, + file_label: trap::Label, + language_prefix: &str, + schema: &'a NodeTypeMap, + ) -> Visitor<'a> { + Visitor { + path, + file_label, + source, + trap_writer, + toplevel_child_counter: 0, + ast_node_info_table_name: format!("{}_ast_node_info", language_prefix), + tokeninfo_table_name: format!("{}_tokeninfo", language_prefix), + schema, + stack: Vec::new(), + } + } + fn record_parse_error( &mut self, error_message: String, full_error_message: String, - loc: Label, + loc: trap::Label, ) { error!("{}", full_error_message); let id = self.trap_writer.fresh_id(); self.trap_writer.add_tuple( "diagnostics", vec![ - Arg::Label(id), - Arg::Int(40), // severity 40 = error - Arg::String("parse_error".to_string()), - Arg::String(error_message), - Arg::String(full_error_message), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Int(40), // severity 40 = error + trap::Arg::String("parse_error".to_string()), + trap::Arg::String(error_message), + trap::Arg::String(full_error_message), + trap::Arg::Label(loc), ], ); } @@ -335,7 +273,8 @@ impl Visitor<'_> { node: Node, ) { let (start_line, start_column, end_line, end_column) = location_for(self.source, node); - let loc = self.trap_writer.location( + let loc = location( + self.trap_writer, self.file_label, start_line, start_column, @@ -374,7 +313,8 @@ impl Visitor<'_> { } let (id, _, child_nodes) = self.stack.pop().expect("Vistor: empty stack"); let (start_line, start_column, end_line, end_column) = location_for(self.source, node); - let loc = self.trap_writer.location( + let loc = location( + self.trap_writer, self.file_label, start_line, start_column, @@ -402,19 +342,19 @@ impl Visitor<'_> { match &table.kind { EntryKind::Token { kind_id, .. } => { self.trap_writer.add_tuple( - &format!("{}_ast_node_info", self.language_prefix), + &self.ast_node_info_table_name, vec![ - Arg::Label(id), - Arg::Label(parent_id), - Arg::Int(parent_index), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Label(parent_id), + trap::Arg::Int(parent_index), + trap::Arg::Label(loc), ], ); self.trap_writer.add_tuple( - &format!("{}_tokeninfo", self.language_prefix), + &self.tokeninfo_table_name, vec![ - Arg::Label(id), - Arg::Int(*kind_id), + trap::Arg::Label(id), + trap::Arg::Int(*kind_id), sliced_source_arg(self.source, node), ], ); @@ -425,15 +365,15 @@ impl Visitor<'_> { } => { if let Some(args) = self.complex_node(&node, fields, &child_nodes, id) { self.trap_writer.add_tuple( - &format!("{}_ast_node_info", self.language_prefix), + &self.ast_node_info_table_name, vec![ - Arg::Label(id), - Arg::Label(parent_id), - Arg::Int(parent_index), - Arg::Label(loc), + trap::Arg::Label(id), + trap::Arg::Label(parent_id), + trap::Arg::Int(parent_index), + trap::Arg::Label(loc), ], ); - let mut all_args = vec![Arg::Label(id)]; + let mut all_args = vec![trap::Arg::Label(id)]; all_args.extend(args); self.trap_writer.add_tuple(table_name, all_args); } @@ -472,9 +412,9 @@ impl Visitor<'_> { node: &Node, fields: &[Field], child_nodes: &[ChildNode], - parent_id: Label, - ) -> Option> { - let mut map: Map<&Option, (&Field, Vec)> = Map::new(); + parent_id: trap::Label, + ) -> Option> { + let mut map: Map<&Option, (&Field, Vec)> = Map::new(); for field in fields { map.insert(&field.name, (field, Vec::new())); } @@ -488,9 +428,9 @@ impl Visitor<'_> { { // We can safely unwrap because type_matches checks the key is in the map. let (int_value, _) = int_mapping.get(&child_node.type_name.kind).unwrap(); - values.push(Arg::Int(*int_value)); + values.push(trap::Arg::Int(*int_value)); } else { - values.push(Arg::Label(child_node.label)); + values.push(trap::Arg::Label(child_node.label)); } } else if field.name.is_some() { let error_message = format!( @@ -569,9 +509,9 @@ impl Visitor<'_> { ); break; } - let mut args = vec![Arg::Label(parent_id)]; + let mut args = vec![trap::Arg::Label(parent_id)]; if *has_index { - args.push(Arg::Int(index)) + args.push(trap::Arg::Int(index)) } args.push(child_value.clone()); self.trap_writer.add_tuple(table_name, args); @@ -625,9 +565,9 @@ impl Visitor<'_> { } // Emit a slice of a source file as an Arg. -fn sliced_source_arg(source: &[u8], n: Node) -> Arg { +fn sliced_source_arg(source: &[u8], n: Node) -> trap::Arg { let range = n.byte_range(); - Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned()) + trap::Arg::String(String::from_utf8_lossy(&source[range.start..range.end]).into_owned()) } // Emit a pair of `TrapEntry`s for the provided node, appropriately calibrated. @@ -699,59 +639,6 @@ fn traverse(tree: &Tree, visitor: &mut Visitor) { } } -pub struct Program(Vec); - -impl fmt::Display for Program { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut text = String::new(); - for trap_entry in &self.0 { - text.push_str(&format!("{}\n", trap_entry)); - } - write!(f, "{}", text) - } -} - -enum TrapEntry { - /// Maps the label to a fresh id, e.g. `#123=*`. - FreshId(Label), - /// Maps the label to a key, e.g. `#7=@"foo"`. - MapLabelToKey(Label, String), - /// foo_bar(arg*) - GenericTuple(String, Vec), - Comment(String), -} -impl fmt::Display for TrapEntry { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - TrapEntry::FreshId(label) => write!(f, "{}=*", label), - TrapEntry::MapLabelToKey(label, key) => { - write!(f, "{}=@\"{}\"", label, key.replace("\"", "\"\"")) - } - TrapEntry::GenericTuple(name, args) => { - write!(f, "{}(", name)?; - for (index, arg) in args.iter().enumerate() { - if index > 0 { - write!(f, ",")?; - } - write!(f, "{}", arg)?; - } - write!(f, ")") - } - TrapEntry::Comment(line) => write!(f, "// {}", line), - } - } -} - -#[derive(Debug, Copy, Clone)] -// Identifiers of the form #0, #1... -struct Label(u32); - -impl fmt::Display for Label { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "#{:x}", self.0) - } -} - // Numeric indices. #[derive(Debug, Copy, Clone)] struct Index(usize); @@ -761,69 +648,3 @@ impl fmt::Display for Index { write!(f, "{}", self.0) } } - -// Some untyped argument to a TrapEntry. -#[derive(Debug, Clone)] -enum Arg { - Label(Label), - Int(usize), - String(String), -} - -const MAX_STRLEN: usize = 1048576; - -impl fmt::Display for Arg { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Arg::Label(x) => write!(f, "{}", x), - Arg::Int(x) => write!(f, "{}", x), - Arg::String(x) => write!( - f, - "\"{}\"", - limit_string(x, MAX_STRLEN).replace("\"", "\"\"") - ), - } - } -} - -/// Limit the length (in bytes) of a string. If the string's length in bytes is -/// less than or equal to the limit then the entire string is returned. Otherwise -/// the string is sliced at the provided limit. If there is a multi-byte character -/// at the limit then the returned slice will be slightly shorter than the limit to -/// avoid splitting that multi-byte character. -fn limit_string(string: &str, max_size: usize) -> &str { - if string.len() <= max_size { - return string; - } - let p = string.as_bytes(); - let mut index = max_size; - // We want to clip the string at [max_size]; however, the character at that position - // may span several bytes. We need to find the first byte of the character. In UTF-8 - // encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte. - // Therefore we decrement the index as long as there are bytes matching this pattern. - // This ensures we cut the string at the border between one character and another. - while index > 0 && (p[index] & 0b11000000) == 0b10000000 { - index -= 1; - } - &string[0..index] -} - -#[test] -fn limit_string_test() { - assert_eq!("hello", limit_string(&"hello world".to_owned(), 5)); - assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6)); - assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5)); -} - -#[test] -fn escape_key_test() { - assert_eq!("foo!", escape_key("foo!")); - assert_eq!("foo{}", escape_key("foo{}")); - assert_eq!("{}", escape_key("{}")); - assert_eq!("", escape_key("")); - assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb")); - assert_eq!( - "/path/to/foo&{}"@#.rb", - escape_key("/path/to/foo&{}\"@#.rb") - ); -} diff --git a/ruby/extractor/src/main.rs b/ruby/extractor/src/main.rs index 7e4aa973518..ca1039e4e0d 100644 --- a/ruby/extractor/src/main.rs +++ b/ruby/extractor/src/main.rs @@ -1,51 +1,19 @@ mod extractor; +mod trap; +#[macro_use] +extern crate lazy_static; extern crate num_cpus; use clap::arg; -use flate2::write::GzEncoder; +use encoding::{self}; use rayon::prelude::*; +use std::borrow::Cow; use std::fs; -use std::io::{BufRead, BufWriter}; +use std::io::BufRead; use std::path::{Path, PathBuf}; use tree_sitter::{Language, Parser, Range}; -enum TrapCompression { - None, - Gzip, -} - -impl TrapCompression { - fn from_env() -> TrapCompression { - match std::env::var("CODEQL_RUBY_TRAP_COMPRESSION") { - Ok(method) => match TrapCompression::from_string(&method) { - Some(c) => c, - None => { - tracing::error!("Unknown compression method '{}'; using gzip.", &method); - TrapCompression::Gzip - } - }, - // Default compression method if the env var isn't set: - Err(_) => TrapCompression::Gzip, - } - } - - fn from_string(s: &str) -> Option { - match s.to_lowercase().as_ref() { - "none" => Some(TrapCompression::None), - "gzip" => Some(TrapCompression::Gzip), - _ => None, - } - } - - fn extension(&self) -> &str { - match self { - TrapCompression::None => "trap", - TrapCompression::Gzip => "trap.gz", - } - } -} - /** * Gets the number of threads the extractor should use, by reading the * CODEQL_THREADS environment variable and using it as described in the @@ -75,6 +43,21 @@ fn num_codeql_threads() -> usize { } } +lazy_static! { + static ref CP_NUMBER: regex::Regex = regex::Regex::new("cp([0-9]+)").unwrap(); +} + +fn encoding_from_name(encoding_name: &str) -> Option<&(dyn encoding::Encoding + Send + Sync)> { + match encoding::label::encoding_from_whatwg_label(encoding_name) { + s @ Some(_) => s, + None => CP_NUMBER.captures(encoding_name).and_then(|cap| { + encoding::label::encoding_from_windows_code_page( + str::parse(cap.get(1).unwrap().as_str()).unwrap(), + ) + }), + } +} + fn main() -> std::io::Result<()> { tracing_subscriber::fmt() .with_target(false) @@ -118,7 +101,7 @@ fn main() -> std::io::Result<()> { .value_of("output-dir") .expect("missing --output-dir"); let trap_dir = PathBuf::from(trap_dir); - let trap_compression = TrapCompression::from_env(); + let trap_compression = trap::Compression::from_env("CODEQL_RUBY_TRAP_COMPRESSION"); let file_list = matches.value_of("file-list").expect("missing --file-list"); let file_list = fs::File::open(file_list)?; @@ -140,8 +123,9 @@ fn main() -> std::io::Result<()> { let path = PathBuf::from(line).canonicalize()?; let src_archive_file = path_for(&src_archive_dir, &path, ""); let mut source = std::fs::read(&path)?; + let mut needs_conversion = false; let code_ranges; - let mut trap_writer = extractor::new_trap_writer(); + let mut trap_writer = trap::Writer::new(); if path.extension().map_or(false, |x| x == "erb") { tracing::info!("scanning: {}", path.display()); extractor::extract( @@ -168,6 +152,43 @@ fn main() -> std::io::Result<()> { } code_ranges = ranges; } else { + if let Some(encoding_name) = scan_coding_comment(&source) { + // If the input is already UTF-8 then there is no need to recode the source + // If the declared encoding is 'binary' or 'ascii-8bit' then it is not clear how + // to interpret characters. In this case it is probably best to leave the input + // unchanged. + if !encoding_name.eq_ignore_ascii_case("utf-8") + && !encoding_name.eq_ignore_ascii_case("ascii-8bit") + && !encoding_name.eq_ignore_ascii_case("binary") + { + if let Some(encoding) = encoding_from_name(&encoding_name) { + needs_conversion = + encoding.whatwg_name().unwrap_or_default() != "utf-8"; + if needs_conversion { + match encoding + .decode(&source, encoding::types::DecoderTrap::Replace) + { + Ok(str) => source = str.as_bytes().to_owned(), + Err(msg) => { + needs_conversion = false; + tracing::warn!( + "{}: character decoding failure: {} ({})", + &path.to_string_lossy(), + msg, + &encoding_name + ); + } + } + } + } else { + tracing::warn!( + "{}: unknown character encoding: '{}'", + &path.to_string_lossy(), + &encoding_name + ); + } + } + } code_ranges = vec![]; } extractor::extract( @@ -180,34 +201,30 @@ fn main() -> std::io::Result<()> { &code_ranges, )?; std::fs::create_dir_all(&src_archive_file.parent().unwrap())?; - std::fs::copy(&path, &src_archive_file)?; - write_trap(&trap_dir, path, trap_writer, &trap_compression) + if needs_conversion { + std::fs::write(&src_archive_file, &source)?; + } else { + std::fs::copy(&path, &src_archive_file)?; + } + write_trap(&trap_dir, path, &trap_writer, trap_compression) }) .expect("failed to extract files"); let path = PathBuf::from("extras"); - let mut trap_writer = extractor::new_trap_writer(); - trap_writer.populate_empty_location(); - write_trap(&trap_dir, path, trap_writer, &trap_compression) + let mut trap_writer = trap::Writer::new(); + extractor::populate_empty_location(&mut trap_writer); + write_trap(&trap_dir, path, &trap_writer, trap_compression) } fn write_trap( trap_dir: &Path, path: PathBuf, - trap_writer: extractor::TrapWriter, - trap_compression: &TrapCompression, + trap_writer: &trap::Writer, + trap_compression: trap::Compression, ) -> std::io::Result<()> { let trap_file = path_for(trap_dir, &path, trap_compression.extension()); std::fs::create_dir_all(&trap_file.parent().unwrap())?; - let trap_file = std::fs::File::create(&trap_file)?; - let mut trap_file = BufWriter::new(trap_file); - match trap_compression { - TrapCompression::None => trap_writer.output(&mut trap_file), - TrapCompression::Gzip => { - let mut compressed_writer = GzEncoder::new(trap_file, flate2::Compression::fast()); - trap_writer.output(&mut compressed_writer) - } - } + trap_writer.write_to_file(&trap_file, trap_compression) } fn scan_erb( @@ -299,3 +316,143 @@ fn path_for(dir: &Path, path: &Path, ext: &str) -> PathBuf { } result } + +fn skip_space(content: &[u8], index: usize) -> usize { + let mut index = index; + while index < content.len() { + let c = content[index] as char; + // white space except \n + let is_space = c == ' ' || ('\t'..='\r').contains(&c) && c != '\n'; + if !is_space { + break; + } + index += 1; + } + index +} + +fn scan_coding_comment(content: &[u8]) -> std::option::Option> { + let mut index = 0; + // skip UTF-8 BOM marker if there is one + if content.len() >= 3 && content[0] == 0xef && content[1] == 0xbb && content[2] == 0xbf { + index += 3; + } + // skip #! line if there is one + if index + 1 < content.len() + && content[index] as char == '#' + && content[index + 1] as char == '!' + { + index += 2; + while index < content.len() && content[index] as char != '\n' { + index += 1 + } + index += 1 + } + index = skip_space(content, index); + + if index >= content.len() || content[index] as char != '#' { + return None; + } + index += 1; + + const CODING: [char; 12] = ['C', 'c', 'O', 'o', 'D', 'd', 'I', 'i', 'N', 'n', 'G', 'g']; + let mut word_index = 0; + while index < content.len() && word_index < CODING.len() && content[index] as char != '\n' { + if content[index] as char == CODING[word_index] + || content[index] as char == CODING[word_index + 1] + { + word_index += 2 + } else { + word_index = 0; + } + index += 1; + } + if word_index < CODING.len() { + return None; + } + index = skip_space(content, index); + + if index < content.len() && content[index] as char != ':' && content[index] as char != '=' { + return None; + } + index += 1; + index = skip_space(content, index); + + let start = index; + while index < content.len() { + let c = content[index] as char; + if c == '-' || c == '_' || c.is_ascii_alphanumeric() { + index += 1; + } else { + break; + } + } + if index > start { + return Some(String::from_utf8_lossy(&content[start..index])); + } + None +} + +#[test] +fn test_scan_coding_comment() { + let text = "# encoding: utf-8"; + let result = scan_coding_comment(text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "#coding:utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "# foo\n# encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, None); + + let text = "# encoding: latin1 encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("latin1".into())); + + let text = "# encoding: nonsense"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("nonsense".into())); + + let text = "# coding = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "# CODING = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "# CoDiNg = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "# blah blahblahcoding = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + // unicode BOM is ignored + let text = "\u{FEFF}# encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "\u{FEFF} # encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "#! /usr/bin/env ruby\n # encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + let text = "\u{FEFF}#! /usr/bin/env ruby\n # encoding: utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + + // A #! must be the first thing on a line, otherwise it's a normal comment + let text = " #! /usr/bin/env ruby encoding = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, Some("utf-8".into())); + let text = " #! /usr/bin/env ruby \n # encoding = utf-8"; + let result = scan_coding_comment(&text.as_bytes()); + assert_eq!(result, None); +} diff --git a/ruby/extractor/src/trap.rs b/ruby/extractor/src/trap.rs new file mode 100644 index 00000000000..35a9b69f255 --- /dev/null +++ b/ruby/extractor/src/trap.rs @@ -0,0 +1,275 @@ +use std::borrow::Cow; +use std::fmt; +use std::io::{BufWriter, Write}; +use std::path::Path; + +use flate2::write::GzEncoder; + +pub struct Writer { + /// The accumulated trap entries + trap_output: Vec, + /// A counter for generating fresh labels + counter: u32, + /// cache of global keys + global_keys: std::collections::HashMap, +} + +impl Writer { + pub fn new() -> Writer { + Writer { + counter: 0, + trap_output: Vec::new(), + global_keys: std::collections::HashMap::new(), + } + } + + pub fn fresh_id(&mut self) -> Label { + let label = Label(self.counter); + self.counter += 1; + self.trap_output.push(Entry::FreshId(label)); + label + } + + /// Gets a label that will hold the unique ID of the passed string at import time. + /// This can be used for incrementally importable TRAP files -- use globally unique + /// strings to compute a unique ID for table tuples. + /// + /// Note: You probably want to make sure that the key strings that you use are disjoint + /// for disjoint column types; the standard way of doing this is to prefix (or append) + /// the column type name to the ID. Thus, you might identify methods in Java by the + /// full ID "methods_com.method.package.DeclaringClass.method(argumentList)". + pub fn global_id(&mut self, key: &str) -> (Label, bool) { + if let Some(label) = self.global_keys.get(key) { + return (*label, false); + } + let label = Label(self.counter); + self.counter += 1; + self.global_keys.insert(key.to_owned(), label); + self.trap_output + .push(Entry::MapLabelToKey(label, key.to_owned())); + (label, true) + } + + pub fn add_tuple(&mut self, table_name: &str, args: Vec) { + self.trap_output + .push(Entry::GenericTuple(table_name.to_owned(), args)) + } + + pub fn comment(&mut self, text: String) { + self.trap_output.push(Entry::Comment(text)); + } + + pub fn write_to_file(&self, path: &Path, compression: Compression) -> std::io::Result<()> { + let trap_file = std::fs::File::create(path)?; + match compression { + Compression::None => { + let mut trap_file = BufWriter::new(trap_file); + self.write_trap_entries(&mut trap_file) + } + Compression::Gzip => { + let trap_file = GzEncoder::new(trap_file, flate2::Compression::fast()); + let mut trap_file = BufWriter::new(trap_file); + self.write_trap_entries(&mut trap_file) + } + } + } + + fn write_trap_entries(&self, file: &mut W) -> std::io::Result<()> { + for trap_entry in &self.trap_output { + writeln!(file, "{}", trap_entry)?; + } + std::io::Result::Ok(()) + } +} + +pub enum Entry { + /// Maps the label to a fresh id, e.g. `#123=*`. + FreshId(Label), + /// Maps the label to a key, e.g. `#7=@"foo"`. + MapLabelToKey(Label, String), + /// foo_bar(arg*) + GenericTuple(String, Vec), + Comment(String), +} + +impl fmt::Display for Entry { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Entry::FreshId(label) => write!(f, "{}=*", label), + Entry::MapLabelToKey(label, key) => { + write!(f, "{}=@\"{}\"", label, key.replace("\"", "\"\"")) + } + Entry::GenericTuple(name, args) => { + write!(f, "{}(", name)?; + for (index, arg) in args.iter().enumerate() { + if index > 0 { + write!(f, ",")?; + } + write!(f, "{}", arg)?; + } + write!(f, ")") + } + Entry::Comment(line) => write!(f, "// {}", line), + } + } +} + +#[derive(Debug, Copy, Clone)] +// Identifiers of the form #0, #1... +pub struct Label(u32); + +impl fmt::Display for Label { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "#{:x}", self.0) + } +} + +// Some untyped argument to a TrapEntry. +#[derive(Debug, Clone)] +pub enum Arg { + Label(Label), + Int(usize), + String(String), +} + +const MAX_STRLEN: usize = 1048576; + +impl fmt::Display for Arg { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Arg::Label(x) => write!(f, "{}", x), + Arg::Int(x) => write!(f, "{}", x), + Arg::String(x) => write!( + f, + "\"{}\"", + limit_string(x, MAX_STRLEN).replace("\"", "\"\"") + ), + } + } +} + +pub struct Program(Vec); + +impl fmt::Display for Program { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut text = String::new(); + for trap_entry in &self.0 { + text.push_str(&format!("{}\n", trap_entry)); + } + write!(f, "{}", text) + } +} + +pub fn full_id_for_file(normalized_path: &str) -> String { + format!("{};sourcefile", escape_key(normalized_path)) +} + +pub fn full_id_for_folder(normalized_path: &str) -> String { + format!("{};folder", escape_key(normalized_path)) +} + +/// Escapes a string for use in a TRAP key, by replacing special characters with +/// HTML entities. +fn escape_key<'a, S: Into>>(key: S) -> Cow<'a, str> { + fn needs_escaping(c: char) -> bool { + matches!(c, '&' | '{' | '}' | '"' | '@' | '#') + } + + let key = key.into(); + if key.contains(needs_escaping) { + let mut escaped = String::with_capacity(2 * key.len()); + for c in key.chars() { + match c { + '&' => escaped.push_str("&"), + '{' => escaped.push_str("{"), + '}' => escaped.push_str("}"), + '"' => escaped.push_str("""), + '@' => escaped.push_str("@"), + '#' => escaped.push_str("#"), + _ => escaped.push(c), + } + } + Cow::Owned(escaped) + } else { + key + } +} + +/// Limit the length (in bytes) of a string. If the string's length in bytes is +/// less than or equal to the limit then the entire string is returned. Otherwise +/// the string is sliced at the provided limit. If there is a multi-byte character +/// at the limit then the returned slice will be slightly shorter than the limit to +/// avoid splitting that multi-byte character. +fn limit_string(string: &str, max_size: usize) -> &str { + if string.len() <= max_size { + return string; + } + let p = string.as_bytes(); + let mut index = max_size; + // We want to clip the string at [max_size]; however, the character at that position + // may span several bytes. We need to find the first byte of the character. In UTF-8 + // encoded data any byte that matches the bit pattern 10XXXXXX is not a start byte. + // Therefore we decrement the index as long as there are bytes matching this pattern. + // This ensures we cut the string at the border between one character and another. + while index > 0 && (p[index] & 0b11000000) == 0b10000000 { + index -= 1; + } + &string[0..index] +} + +#[derive(Clone, Copy)] +pub enum Compression { + None, + Gzip, +} + +impl Compression { + pub fn from_env(var_name: &str) -> Compression { + match std::env::var(var_name) { + Ok(method) => match Compression::from_string(&method) { + Some(c) => c, + None => { + tracing::error!("Unknown compression method '{}'; using gzip.", &method); + Compression::Gzip + } + }, + // Default compression method if the env var isn't set: + Err(_) => Compression::Gzip, + } + } + + pub fn from_string(s: &str) -> Option { + match s.to_lowercase().as_ref() { + "none" => Some(Compression::None), + "gzip" => Some(Compression::Gzip), + _ => None, + } + } + + pub fn extension(&self) -> &str { + match self { + Compression::None => "trap", + Compression::Gzip => "trap.gz", + } + } +} + +#[test] +fn limit_string_test() { + assert_eq!("hello", limit_string(&"hello world".to_owned(), 5)); + assert_eq!("hi ☹", limit_string(&"hi ☹☹".to_owned(), 6)); + assert_eq!("hi ", limit_string(&"hi ☹☹".to_owned(), 5)); +} + +#[test] +fn escape_key_test() { + assert_eq!("foo!", escape_key("foo!")); + assert_eq!("foo{}", escape_key("foo{}")); + assert_eq!("{}", escape_key("{}")); + assert_eq!("", escape_key("")); + assert_eq!("/path/to/foo.rb", escape_key("/path/to/foo.rb")); + assert_eq!( + "/path/to/foo&{}"@#.rb", + escape_key("/path/to/foo&{}\"@#.rb") + ); +} diff --git a/ruby/generator/Cargo.toml b/ruby/generator/Cargo.toml index 806dce9d609..5a70c43d7b3 100644 --- a/ruby/generator/Cargo.toml +++ b/ruby/generator/Cargo.toml @@ -12,4 +12,4 @@ node-types = { path = "../node-types" } tracing = "0.1" tracing-subscriber = { version = "0.3.3", features = ["env-filter"] } tree-sitter-embedded-template = { git = "https://github.com/tree-sitter/tree-sitter-embedded-template.git", rev = "1a538da253d73f896b9f6c0c7d79cda58791ac5c" } -tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "5b305c3cd32db10494cedd2743de6bbe32f1a573" } +tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "e75d04404c9dd71ad68850d5c672b226d5e694f3" } diff --git a/ruby/ql/consistency-queries/DataFlowConsistency.ql b/ruby/ql/consistency-queries/DataFlowConsistency.ql index 000bbc57899..80eb90913d0 100644 --- a/ruby/ql/consistency-queries/DataFlowConsistency.ql +++ b/ruby/ql/consistency-queries/DataFlowConsistency.ql @@ -10,6 +10,6 @@ private class MyConsistencyConfiguration extends ConsistencyConfiguration { or n instanceof SummaryNode or - n instanceof HashSplatArgumentsNode + n instanceof SynthHashSplatArgumentNode } } diff --git a/ruby/ql/lib/CHANGELOG.md b/ruby/ql/lib/CHANGELOG.md index 1b060e46141..09d016efb49 100644 --- a/ruby/ql/lib/CHANGELOG.md +++ b/ruby/ql/lib/CHANGELOG.md @@ -1,3 +1,19 @@ +## 0.3.2 + +### Minor Analysis Improvements + +* Calls to `Arel.sql` are now recognised as propagating taint from their argument. +* Calls to `ActiveRecord::Relation#annotate` are now recognized as `SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. + +## 0.3.1 + +### Minor Analysis Improvements + +* Fixed a bug causing every expression in the database to be considered a system-command execution sink when calls to any of the following methods exist: + * The `spawn`, `fspawn`, `popen4`, `pspawn`, `system`, `_pspawn` methods and the backtick operator from the `POSIX::spawn` gem. + * The `execute_command`, `rake`, `rails_command`, and `git` methods in `Rails::Generation::Actions`. +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). + ## 0.3.0 ### Deprecated APIs diff --git a/ruby/ql/lib/change-notes/2022-06-22-sensitive-common-words.md b/ruby/ql/lib/change-notes/2022-06-22-sensitive-common-words.md deleted file mode 100644 index 9102d58abdb..00000000000 --- a/ruby/ql/lib/change-notes/2022-06-22-sensitive-common-words.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: minorAnalysis ---- -* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). diff --git a/ruby/ql/lib/change-notes/2022-08-04-active-record-writes.md b/ruby/ql/lib/change-notes/2022-08-04-active-record-writes.md new file mode 100644 index 00000000000..b2b4d0bc2ad --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-08-04-active-record-writes.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Calls to `ActiveRecord::Base.create` and `ActiveRecord::Base.update` are now + recognised as write accesses. diff --git a/ruby/ql/lib/change-notes/2022-08-04-mime-type.md b/ruby/ql/lib/change-notes/2022-08-04-mime-type.md new file mode 100644 index 00000000000..033e8ed626c --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-08-04-mime-type.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Arguments to `Mime::Type#match?` and `Mime::Type#=~` are now recognised as + regular expression sources. diff --git a/ruby/ql/lib/change-notes/2022-08-05-active-record-associations.md b/ruby/ql/lib/change-notes/2022-08-05-active-record-associations.md new file mode 100644 index 00000000000..9fa4d0a6cd5 --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-08-05-active-record-associations.md @@ -0,0 +1,6 @@ +--- +category: minorAnalysis +--- +* Calls to methods generated by ActiveRecord associations are now recognised as + instantiations of ActiveRecord objects. This increases the sensitivity of + queries such as `rb/sql-injection` and `rb/stored-xss`. diff --git a/ruby/ql/lib/change-notes/released/0.3.1.md b/ruby/ql/lib/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..59f378bbb32 --- /dev/null +++ b/ruby/ql/lib/change-notes/released/0.3.1.md @@ -0,0 +1,8 @@ +## 0.3.1 + +### Minor Analysis Improvements + +* Fixed a bug causing every expression in the database to be considered a system-command execution sink when calls to any of the following methods exist: + * The `spawn`, `fspawn`, `popen4`, `pspawn`, `system`, `_pspawn` methods and the backtick operator from the `POSIX::spawn` gem. + * The `execute_command`, `rake`, `rails_command`, and `git` methods in `Rails::Generation::Actions`. +* Improved modeling of sensitive data sources, so common words like `certain` and `secretary` are no longer considered a certificate and a secret (respectively). diff --git a/ruby/ql/lib/change-notes/released/0.3.2.md b/ruby/ql/lib/change-notes/released/0.3.2.md new file mode 100644 index 00000000000..bdb97f6d3ce --- /dev/null +++ b/ruby/ql/lib/change-notes/released/0.3.2.md @@ -0,0 +1,6 @@ +## 0.3.2 + +### Minor Analysis Improvements + +* Calls to `Arel.sql` are now recognised as propagating taint from their argument. +* Calls to `ActiveRecord::Relation#annotate` are now recognized as `SqlExecution`s so that it will be considered as a sink for queries like rb/sql-injection. diff --git a/ruby/ql/lib/codeql-pack.release.yml b/ruby/ql/lib/codeql-pack.release.yml index 95f6e3a0ba6..18c64250f42 100644 --- a/ruby/ql/lib/codeql-pack.release.yml +++ b/ruby/ql/lib/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.3.0 +lastReleaseVersion: 0.3.2 diff --git a/ruby/ql/lib/codeql/ruby/Frameworks.qll b/ruby/ql/lib/codeql/ruby/Frameworks.qll index 87b8a3f61a2..69ba758a767 100644 --- a/ruby/ql/lib/codeql/ruby/Frameworks.qll +++ b/ruby/ql/lib/codeql/ruby/Frameworks.qll @@ -10,6 +10,7 @@ private import codeql.ruby.frameworks.ActiveStorage private import codeql.ruby.frameworks.ActionView private import codeql.ruby.frameworks.ActiveSupport private import codeql.ruby.frameworks.Archive +private import codeql.ruby.frameworks.Arel private import codeql.ruby.frameworks.GraphQL private import codeql.ruby.frameworks.Rails private import codeql.ruby.frameworks.Railties diff --git a/ruby/ql/lib/codeql/ruby/Regexp.qll b/ruby/ql/lib/codeql/ruby/Regexp.qll index b6872d2bca3..a0454d0f5fd 100644 --- a/ruby/ql/lib/codeql/ruby/Regexp.qll +++ b/ruby/ql/lib/codeql/ruby/Regexp.qll @@ -92,20 +92,32 @@ private class ParsedStringRegExp extends RegExp { override string getFlags() { none() } } +/** Provides a class for modeling regular expression interpretations. */ +module RegExpInterpretation { + /** + * A node that is not a regular expression literal, but is used in places that + * may interpret it as one. Instances of this class are typically strings that + * flow to method calls like `RegExp.new`. + */ + abstract class Range extends DataFlow::Node { } +} + /** - * Holds if `source` may be interpreted as a regular expression. + * A node interpreted as a regular expression. */ -private predicate isInterpretedAsRegExp(DataFlow::Node source) { - // The first argument to an invocation of `Regexp.new` or `Regexp.compile`. - source = API::getTopLevelMember("Regexp").getAMethodCall(["compile", "new"]).getArgument(0) - or - // The argument of a call that coerces the argument to a regular expression. - exists(DataFlow::CallNode mce | - mce.getMethodName() = ["match", "match?"] and - source = mce.getArgument(0) and - // exclude https://ruby-doc.org/core-2.4.0/Regexp.html#method-i-match - not mce.getReceiver().asExpr().getExpr() instanceof AST::RegExpLiteral - ) +class StdLibRegExpInterpretation extends RegExpInterpretation::Range { + StdLibRegExpInterpretation() { + // The first argument to an invocation of `Regexp.new` or `Regexp.compile`. + this = API::getTopLevelMember("Regexp").getAMethodCall(["compile", "new"]).getArgument(0) + or + // The argument of a call that coerces the argument to a regular expression. + exists(DataFlow::CallNode mce | + mce.getMethodName() = ["match", "match?"] and + this = mce.getArgument(0) and + // exclude https://ruby-doc.org/core-2.4.0/Regexp.html#method-i-match + not mce.getReceiver().asExpr().getExpr() instanceof AST::RegExpLiteral + ) + } } private class RegExpConfiguration extends Configuration { @@ -120,7 +132,7 @@ private class RegExpConfiguration extends Configuration { ) } - override predicate isSink(DataFlow::Node sink) { isInterpretedAsRegExp(sink) } + override predicate isSink(DataFlow::Node sink) { sink instanceof RegExpInterpretation::Range } override predicate isSanitizer(DataFlow::Node node) { // stop flow if `node` is receiver of diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll index fb7cb3737b9..53355695e57 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Constant.qll @@ -36,7 +36,7 @@ private import ExprNodes * constant value in some cases. */ private module Propagation { - private ExprCfgNode getSource(VariableReadAccessCfgNode read) { + ExprCfgNode getSource(VariableReadAccessCfgNode read) { exists(Ssa::WriteDefinition def | def.assigns(result) and read = def.getARead() @@ -509,3 +509,53 @@ private module Cached { } import Cached + +/** + * Holds if the control flow node `e` refers to an array constructed from the + * array literal `arr`. + * Example: + * ```rb + * [1, 2, 3] + * C = [1, 2, 3]; C + * x = [1, 2, 3]; x + * ``` + */ +predicate isArrayConstant(ExprCfgNode e, ArrayLiteralCfgNode arr) { + // [...] + e = arr + or + // e = [...]; e + isArrayConstant(getSource(e), arr) + or + isArrayExpr(e.getExpr(), arr) +} + +/** + * Holds if the expression `e` refers to an array constructed from the array literal `arr`. + */ +private predicate isArrayExpr(Expr e, ArrayLiteralCfgNode arr) { + // e = [...] + e = arr.getExpr() + or + // Like above, but handles the desugaring of array literals to Array.[] calls. + e.getDesugared() = arr.getExpr() + or + // A = [...]; A + // A = a; A + isArrayExpr(e.(ConstantReadAccess).getValue(), arr) + or + // Recurse via CFG nodes. Necessary for example in: + // a = [...] + // A = a + // A + // + // We map from A to a via ConstantReadAccess::getValue, yielding the Expr a. + // To get to [...] we need to go via getSource(ExprCfgNode e), so we find a + // CFG node for a and call `isArrayConstant`. + // + // The use of `forex` is intended to ensure that a is an array constant in all + // control flow paths. + // Note(hmac): I don't think this is necessary, as `getSource` will not return + // results if the source is a phi node. + forex(ExprCfgNode n | n = e.getAControlFlowNode() | isArrayConstant(n, arr)) +} diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index efb69af39eb..526cc5adaf9 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -348,7 +348,7 @@ module ExprNodes { /** Gets an argument of this call. */ final ExprCfgNode getAnArgument() { result = this.getArgument(_) } - /** Gets the the keyword argument whose key is `keyword` of this call. */ + /** Gets the keyword argument whose key is `keyword` of this call. */ final ExprCfgNode getKeywordArgument(string keyword) { exists(PairCfgNode n | e.hasCfgChild(e.getAnArgument(), this, n) and @@ -357,6 +357,14 @@ module ExprNodes { ) } + /** + * Gets the `n`th positional argument of this call. + * Unlike `getArgument`, this excludes keyword arguments. + */ + final ExprCfgNode getPositionalArgument(int n) { + result = this.getArgument(n) and not result instanceof PairCfgNode + } + /** Gets the number of arguments of this call. */ final int getNumberOfArguments() { result = e.getNumberOfArguments() } @@ -374,6 +382,9 @@ module ExprNodes { MethodCallCfgNode() { super.getExpr() instanceof MethodCall } override MethodCall getExpr() { result = super.getExpr() } + + /** Gets the name of this method call. */ + string getMethodName() { result = this.getExpr().getMethodName() } } private class CaseExprChildMapping extends ExprChildMapping, CaseExpr { diff --git a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll b/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll index 47fcd24883c..7d0dd10c084 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/internal/ControlFlowGraphImplShared.qll +++ b/ruby/ql/lib/codeql/ruby/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/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll index d55bd9af278..c79e4311489 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/BarrierGuards.qll @@ -3,6 +3,10 @@ private import ruby private import codeql.ruby.DataFlow private import codeql.ruby.CFG +private import codeql.ruby.controlflow.CfgNodes +private import codeql.ruby.dataflow.SSA +private import codeql.ruby.ast.internal.Constant +private import codeql.ruby.InclusionTests private predicate stringConstCompare(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) { exists(CfgNodes::ExprNodes::ComparisonOperationCfgNode c | @@ -61,19 +65,7 @@ deprecated class StringConstCompare extends DataFlow::BarrierGuard, // The value of the condition that results in the node being validated. private boolean checkedBranch; - StringConstCompare() { - exists(CfgNodes::ExprNodes::StringLiteralCfgNode strLitNode | - this.getExpr() instanceof EqExpr and checkedBranch = true - or - this.getExpr() instanceof CaseEqExpr and checkedBranch = true - or - this.getExpr() instanceof NEExpr and checkedBranch = false - | - this.getLeftOperand() = strLitNode and this.getRightOperand() = checkedNode - or - this.getLeftOperand() = checkedNode and this.getRightOperand() = strLitNode - ) - } + StringConstCompare() { stringConstCompare(this, checkedNode, checkedBranch) } override predicate checks(CfgNode expr, boolean branch) { expr = checkedNode and branch = checkedBranch @@ -81,15 +73,19 @@ deprecated class StringConstCompare extends DataFlow::BarrierGuard, } private predicate stringConstArrayInclusionCall(CfgNodes::ExprCfgNode g, CfgNode e, boolean branch) { - exists(CfgNodes::ExprNodes::MethodCallCfgNode mc, ArrayLiteral aLit | - mc = g and - mc.getExpr().getMethodName() = "include?" and - [mc.getExpr().getReceiver(), mc.getExpr().getReceiver().(ConstantReadAccess).getValue()] = aLit + exists(InclusionTest t | + t.asExpr() = g and + e = t.getContainedNode().asExpr() and + branch = t.getPolarity() | - forall(Expr elem | elem = aLit.getAnElement() | elem instanceof StringLiteral) and - mc.getArgument(0) = e - ) and - branch = true + exists(ExprNodes::ArrayLiteralCfgNode arr | + isArrayConstant(t.getContainerNode().asExpr(), arr) + | + forall(ExprCfgNode elem | elem = arr.getAnArgument() | + elem instanceof ExprNodes::StringLiteralCfgNode + ) + ) + ) } /** @@ -132,16 +128,7 @@ deprecated class StringConstArrayInclusionCall extends DataFlow::BarrierGuard, CfgNodes::ExprNodes::MethodCallCfgNode { private CfgNode checkedNode; - StringConstArrayInclusionCall() { - exists(ArrayLiteral aLit | - this.getExpr().getMethodName() = "include?" and - [this.getExpr().getReceiver(), this.getExpr().getReceiver().(ConstantReadAccess).getValue()] = - aLit - | - forall(Expr elem | elem = aLit.getAnElement() | elem instanceof StringLiteral) and - this.getArgument(0) = checkedNode - ) - } + StringConstArrayInclusionCall() { stringConstArrayInclusionCall(this, checkedNode, true) } override predicate checks(CfgNode expr, boolean branch) { expr = checkedNode and branch = true } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll index 9bfc4f5c9b6..3ec315b5d9b 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll @@ -65,7 +65,12 @@ class DataFlowCallable extends TDataFlowCallable { string toString() { result = [this.asCallable().toString(), this.asLibraryCallable()] } /** Gets the location of this callable. */ - Location getLocation() { result = this.asCallable().getLocation() } + Location getLocation() { + result = this.asCallable().getLocation() + or + this instanceof TLibraryCallable and + result instanceof EmptyLocation + } } /** diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..340bfe280b7 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll index a076f7a2e45..340bfe280b7 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll index a076f7a2e45..340bfe280b7 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplForLibraries.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index a6c73b1d893..2386fd0bf6e 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -227,6 +227,9 @@ private module Cached { } or TSelfParameterNode(MethodBase m) or TBlockParameterNode(MethodBase m) or + TSynthHashSplatParameterNode(DataFlowCallable c) { + isParameterNode(_, c, any(ParameterPosition p | p.isKeyword(_))) + } or TExprPostUpdateNode(CfgNodes::ExprCfgNode n) { n instanceof Argument or n = any(CfgNodes::ExprNodes::InstanceVariableAccessCfgNode v).getReceiver() @@ -240,12 +243,13 @@ private module Cached { TSummaryParameterNode(FlowSummaryImpl::Public::SummarizedCallable c, ParameterPosition pos) { FlowSummaryImpl::Private::summaryParameterNodeRange(c, pos) } or - THashSplatArgumentsNode(CfgNodes::ExprNodes::CallCfgNode c) { + TSynthHashSplatArgumentNode(CfgNodes::ExprNodes::CallCfgNode c) { exists(Argument arg | arg.isArgumentOf(c, any(ArgumentPosition pos | pos.isKeyword(_)))) } class TParameterNode = - TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or TSummaryParameterNode; + TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or + TSynthHashSplatParameterNode or TSummaryParameterNode; private predicate defaultValueFlow(NamedParameter p, ExprNode e) { p.(OptionalParameter).getDefaultValue() = e.getExprNode().getExpr() @@ -328,18 +332,21 @@ private module Cached { cached predicate isLocalSourceNode(Node n) { - n instanceof ParameterNode - or - n instanceof PostUpdateNodes::ExprPostUpdateNode - or - // Nodes that can't be reached from another entry definition or expression. - not reachedFromExprOrEntrySsaDef(n) - or - // Ensure all entry SSA definitions are local sources -- for parameters, this - // is needed by type tracking. Note that when the parameter has a default value, - // it will be reachable from an expression (the default value) and therefore - // won't be caught by the rule above. - entrySsaDefinition(n) + not n instanceof SynthHashSplatParameterNode and + ( + n instanceof ParameterNode + or + n instanceof PostUpdateNodes::ExprPostUpdateNode + or + // Nodes that can't be reached from another entry definition or expression. + not reachedFromExprOrEntrySsaDef(n) + or + // Ensure all entry SSA definitions are local sources -- for parameters, this + // is needed by type tracking. Note that when the parameter has a default value, + // it will be reachable from an expression (the default value) and therefore + // won't be caught by the rule above. + entrySsaDefinition(n) + ) } cached @@ -415,7 +422,9 @@ predicate nodeIsHidden(Node n) { or n instanceof SynthReturnNode or - n instanceof HashSplatArgumentsNode + n instanceof SynthHashSplatParameterNode + or + n instanceof SynthHashSplatArgumentNode } /** An SSA definition, viewed as a node in a data flow graph. */ @@ -470,10 +479,13 @@ private module ParameterNodes { abstract class ParameterNodeImpl extends NodeImpl { abstract Parameter getParameter(); - abstract predicate isSourceParameterOf(Callable c, ParameterPosition pos); + abstract predicate isParameterOf(DataFlowCallable c, ParameterPosition pos); - predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { - this.isSourceParameterOf(c.asCallable(), pos) + final predicate isSourceParameterOf(Callable c, ParameterPosition pos) { + exists(DataFlowCallable callable | + this.isParameterOf(callable, pos) and + c = callable.asCallable() + ) } } @@ -488,21 +500,23 @@ private module ParameterNodes { override Parameter getParameter() { result = parameter } - override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { - exists(int i | pos.isPositional(i) and c.getParameter(i) = parameter | - parameter instanceof SimpleParameter - or - parameter instanceof OptionalParameter - ) - or - parameter = - any(KeywordParameter kp | - c.getAParameter() = kp and - pos.isKeyword(kp.getName()) + override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + exists(Callable callable | callable = c.asCallable() | + exists(int i | pos.isPositional(i) and callable.getParameter(i) = parameter | + parameter instanceof SimpleParameter + or + parameter instanceof OptionalParameter ) - or - parameter = c.getAParameter().(HashSplatParameter) and - pos.isHashSplat() + or + parameter = + any(KeywordParameter kp | + callable.getAParameter() = kp and + pos.isKeyword(kp.getName()) + ) + or + parameter = callable.getAParameter().(HashSplatParameter) and + pos.isHashSplat() + ) } override CfgScope getCfgScope() { result = parameter.getCallable() } @@ -525,8 +539,8 @@ private module ParameterNodes { override Parameter getParameter() { none() } - override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { - method = c and pos.isSelf() + override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + method = c.asCallable() and pos.isSelf() } override CfgScope getCfgScope() { result = method } @@ -551,8 +565,8 @@ private module ParameterNodes { result = method.getAParameter() and result instanceof BlockParameter } - override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { - c = method and pos.isBlock() + override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + c.asCallable() = method and pos.isBlock() } override CfgScope getCfgScope() { result = method } @@ -570,6 +584,73 @@ private module ParameterNodes { } } + /** + * For all methods containing keyword parameters, we construct a synthesized + * (hidden) parameter node to contain all keyword arguments. This allows us + * to handle cases like + * + * ```rb + * def foo(p1:, p2:) + * sink(p1) + * sink(p2) + * end + * + * args = {:p1 => taint(1), :p2 => taint(2) } + * foo(**args) + * ``` + * + * by adding read steps out of the synthesized parameter node to the relevant + * keyword parameters. + * + * Note that this will introduce a bit of redundancy in cases like + * + * ```rb + * foo(p1: taint(1), p2: taint(2)) + * ``` + * + * where direct keyword matching is possible, since we construct a synthesized hash + * splat argument (`SynthHashSplatArgumentNode`) at the call site, which means that + * `taint(1)` will flow into `p1` both via normal keyword matching and via the synthesized + * nodes (and similarly for `p2`). However, this redunancy is OK since + * (a) it means that type-tracking through keyword arguments also works in most cases, + * (b) read/store steps can be avoided when direct keyword matching is possible, and + * hence access path limits are not a concern, and + * (c) since the synthesized nodes are hidden, the reported data-flow paths will be + * collapsed anyway. + */ + class SynthHashSplatParameterNode extends ParameterNodeImpl, TSynthHashSplatParameterNode { + private DataFlowCallable callable; + + SynthHashSplatParameterNode() { this = TSynthHashSplatParameterNode(callable) } + + /** + * Gets a keyword parameter that will be the result of reading `c` out of this + * synthesized node. + */ + ParameterNode getAKeywordParameter(ContentSet c) { + exists(string name | + isParameterNode(result, callable, any(ParameterPosition p | p.isKeyword(name))) + | + c = getKeywordContent(name) or + c.isSingleton(TUnknownElementContent()) + ) + } + + final override Parameter getParameter() { none() } + + final override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + c = callable and pos.isHashSplat() + } + + final override CfgScope getCfgScope() { result = callable.asCallable() } + + final override DataFlowCallable getEnclosingCallable() { result = callable } + + final override Location getLocationImpl() { result = callable.getLocation() } + + final override string toStringImpl() { result = "**kwargs" } + } + /** A parameter for a library callable with a flow summary. */ class SummaryParameterNode extends ParameterNodeImpl, TSummaryParameterNode { private FlowSummaryImpl::Public::SummarizedCallable sc; @@ -579,8 +660,6 @@ private module ParameterNodes { override Parameter getParameter() { none() } - override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { none() } - override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { sc = c.asLibraryCallable() and pos = pos_ } @@ -689,10 +768,10 @@ private module ArgumentNodes { * part of the method signature, such that those cannot end up in the hash-splat * parameter. */ - class HashSplatArgumentsNode extends ArgumentNode, THashSplatArgumentsNode { + class SynthHashSplatArgumentNode extends ArgumentNode, TSynthHashSplatArgumentNode { CfgNodes::ExprNodes::CallCfgNode c; - HashSplatArgumentsNode() { this = THashSplatArgumentsNode(c) } + SynthHashSplatArgumentNode() { this = TSynthHashSplatArgumentNode(c) } override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { this.sourceArgumentOf(call.asCall(), pos) @@ -704,10 +783,10 @@ private module ArgumentNodes { } } - private class HashSplatArgumentsNodeImpl extends NodeImpl, THashSplatArgumentsNode { + private class SynthHashSplatArgumentNodeImpl extends NodeImpl, TSynthHashSplatArgumentNode { CfgNodes::ExprNodes::CallCfgNode c; - HashSplatArgumentsNodeImpl() { this = THashSplatArgumentsNode(c) } + SynthHashSplatArgumentNodeImpl() { this = TSynthHashSplatArgumentNode(c) } override CfgScope getCfgScope() { result = c.getExpr().getCfgScope() } @@ -929,7 +1008,7 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { or // Wrap all keyword arguments in a synthesized hash-splat argument node exists(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition keywordPos, string name | - node2 = THashSplatArgumentsNode(call) and + node2 = TSynthHashSplatArgumentNode(call) and node1.asExpr().(Argument).isArgumentOf(call, keywordPos) and keywordPos.isKeyword(name) and c = getKeywordContent(name) @@ -962,6 +1041,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) { )) ) or + node2 = node1.(SynthHashSplatParameterNode).getAKeywordParameter(c) + or FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index 730445d99ac..e35a85de8e2 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -71,7 +71,15 @@ class CallNode extends LocalSourceNode, ExprNode { /** Gets the data-flow node corresponding to the named argument of the call corresponding to this data-flow node */ ExprNode getKeywordArgument(string name) { result.getExprNode() = node.getKeywordArgument(name) } - /** Gets the name of the the method called by the method call (if any) corresponding to this data-flow node */ + /** + * Gets the `n`th positional argument of this call. + * Unlike `getArgument`, this excludes keyword arguments. + */ + final ExprNode getPositionalArgument(int n) { + result.getExprNode() = node.getPositionalArgument(n) + } + + /** Gets the name of the method called by the method call (if any) corresponding to this data-flow node */ string getMethodName() { result = node.getExpr().(MethodCall).getMethodName() } /** Gets the number of arguments of this call. */ diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll index 90b00ecde3f..b32d368dad3 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll @@ -83,7 +83,7 @@ class ActionControllerActionMethod extends Method, HTTP::Server::RequestHandler: * Gets a route to this handler, if one exists. * May return multiple results. */ - ActionDispatch::Route getARoute() { + ActionDispatch::Routing::Route getARoute() { exists(string name | isRoute(result, name, controllerClass) and isActionControllerMethod(this, name, controllerClass) @@ -93,10 +93,10 @@ class ActionControllerActionMethod extends Method, HTTP::Server::RequestHandler: pragma[nomagic] private predicate isRoute( - ActionDispatch::Route route, string name, ActionControllerControllerClass controllerClass + ActionDispatch::Routing::Route route, string name, ActionControllerControllerClass controllerClass ) { route.getController() + "_controller" = - ActionDispatch::underscore(namespaceDeclaration(controllerClass)) and + ActionDispatch::Routing::underscore(namespaceDeclaration(controllerClass)) and name = route.getAction() } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll index bd16ef3796e..e9ff9d5c86a 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionDispatch.qll @@ -1,959 +1,1017 @@ /** - * Models routing configuration specified using the `ActionDispatch` library, which is part of Rails. + * Models the `ActionDispatch` HTTP library, which is part of Rails. + * Version: 7.1.0. */ private import codeql.ruby.AST private import codeql.ruby.Concepts private import codeql.ruby.DataFlow +private import codeql.ruby.Regexp as RE +private import codeql.ruby.dataflow.FlowSummary +private import codeql.ruby.frameworks.data.ModelsAsData /** - * Models routing configuration specified using the `ActionDispatch` library, which is part of Rails. + * Models the `ActionDispatch` HTTP library, which is part of Rails. + * Version: 7.1.0. */ module ActionDispatch { /** - * A block that defines some routes. - * Route blocks can contribute to the path or controller namespace of their child routes. - * For example, in the block below - * ```rb - * scope path: "/admin" do - * get "/dashboard", to: "admin_dashboard#show" - * end - * ``` - * the route defined by the call to `get` has the full path `/admin/dashboard`. - * We track these contributions via `getPathComponent` and `getControllerComponent`. + * Type summaries for the `Mime::Type` class, i.e. method calls that produce new + * `Mime::Type` instances. */ - abstract private class RouteBlock extends TRouteBlock { - /** - * Gets the name of a primary CodeQL class to which this route block belongs. - */ - string getAPrimaryQlClass() { result = "RouteBlock" } - - /** - * Gets a string representation of this route block. - */ - string toString() { none() } - - /** - * Gets a `Stmt` within this route block. - */ - abstract Stmt getAStmt(); - - /** - * Gets the parent of this route block, if one exists. - */ - abstract RouteBlock getParent(); - - /** - * Gets the `n`th parent of this route block. - * The zeroth parent is this block, the first parent is the direct parent of this block, etc. - */ - RouteBlock getParent(int n) { - if n = 0 then result = this else result = this.getParent().getParent(n - 1) - } - - /** - * Gets the component of the path defined by this block, if it exists. - */ - abstract string getPathComponent(); - - /** - * Gets the component of the controller namespace defined by this block, if it exists. - */ - abstract string getControllerComponent(); - - /** - * Gets the location of this route block. - */ - abstract Location getLocation(); - } - - /** - * A route block that is not the top-level block. - * This block will always have a parent. - */ - abstract private class NestedRouteBlock extends RouteBlock { - RouteBlock parent; - - override RouteBlock getParent() { result = parent } - - override string getAPrimaryQlClass() { result = "NestedRouteBlock" } - } - - /** - * A top-level routes block. - * ```rb - * Rails.application.routes.draw do - * ... - * end - * ``` - */ - private class TopLevelRouteBlock extends RouteBlock, TTopLevelRouteBlock { - MethodCall call; - // Routing blocks create scopes which define the namespace for controllers and paths, - // though they can be overridden in various ways. - // The namespaces can differ, so we track them separately. - Block block; - - TopLevelRouteBlock() { this = TTopLevelRouteBlock(_, call, block) } - - override string getAPrimaryQlClass() { result = "TopLevelRouteBlock" } - - Block getBlock() { result = block } - - override Stmt getAStmt() { result = block.getAStmt() } - - override RouteBlock getParent() { none() } - - override string toString() { result = call.toString() } - - override Location getLocation() { result = call.getLocation() } - - override string getPathComponent() { none() } - - override string getControllerComponent() { none() } - } - - /** - * A route block defined by a call to `constraints`. - * ```rb - * constraints(foo: /some_regex/) do - * get "/posts/:foo", to "posts#something" - * end - * ``` - * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-constraints - */ - private class ConstraintsRouteBlock extends NestedRouteBlock, TConstraintsRouteBlock { - private Block block; - private MethodCall call; - - ConstraintsRouteBlock() { this = TConstraintsRouteBlock(parent, call, block) } - - override string getAPrimaryQlClass() { result = "ConstraintsRouteBlock" } - - override Stmt getAStmt() { result = block.getAStmt() } - - override string getPathComponent() { result = "" } - - override string getControllerComponent() { result = "" } - - override string toString() { result = call.toString() } - - override Location getLocation() { result = call.getLocation() } - } - - /** - * A route block defined by a call to `scope`. - * ```rb - * scope(path: "/some_path", module: "some_module") do - * get "/posts/:foo", to "posts#something" - * end - * ``` - * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-scope - */ - private class ScopeRouteBlock extends NestedRouteBlock, TScopeRouteBlock { - private MethodCall call; - private Block block; - - ScopeRouteBlock() { this = TScopeRouteBlock(parent, call, block) } - - override string getAPrimaryQlClass() { result = "ScopeRouteBlock" } - - override Stmt getAStmt() { result = block.getAStmt() } - - override string toString() { result = call.toString() } - - override Location getLocation() { result = call.getLocation() } - - override string getPathComponent() { - call.getKeywordArgument("path").getConstantValue().isStringlikeValue(result) - or - not exists(call.getKeywordArgument("path")) and - call.getArgument(0).getConstantValue().isStringlikeValue(result) - } - - override string getControllerComponent() { - call.getKeywordArgument(["controller", "module"]).getConstantValue().isStringlikeValue(result) + private class MimeTypeTypeSummary extends ModelInput::TypeModelCsv { + override predicate row(string row) { + // package1;type1;package2;type2;path + row = + [ + // Mime[type] : Mime::Type (omitted) + // Method names with brackets like [] cannot be represented in MaD. + // Mime.fetch(type) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Method[fetch].ReturnValue", + // Mime::Type.new(str) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Member[Type].Instance", + // Mime::Type.lookup(str) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Member[Type].Method[lookup].ReturnValue", + // Mime::Type.lookup_by_extension(str) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Member[Type].Method[lookup_by_extension].ReturnValue", + // Mime::Type.register(str) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Member[Type].Method[register].ReturnValue", + // Mime::Type.register_alias(str) : Mime::Type + "actiondispatch;Mime::Type;;;Member[Mime].Member[Type].Method[register_alias].ReturnValue", + ] } } /** - * A route block defined by a call to `resources`. - * ```rb - * resources :articles do - * get "/comments", to "comments#index" - * end - * ``` - * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resources + * An argument to `Mime::Type#match?`, which is converted to a RegExp via + * `Regexp.new`. */ - private class ResourcesRouteBlock extends NestedRouteBlock, TResourcesRouteBlock { - private MethodCall call; - private Block block; - - ResourcesRouteBlock() { this = TResourcesRouteBlock(parent, call, block) } - - override string getAPrimaryQlClass() { result = "ResourcesRouteBlock" } - - override Stmt getAStmt() { result = block.getAStmt() } - - /** - * Gets the `resources` call that gives rise to this route block. - */ - MethodCall getDefiningMethodCall() { result = call } - - override string getPathComponent() { - exists(string resource | call.getArgument(0).getConstantValue().isStringlikeValue(resource) | - result = resource + "/:" + singularize(resource) + "_id" - ) + class MimeTypeMatchRegExpInterpretation extends RE::RegExpInterpretation::Range { + MimeTypeMatchRegExpInterpretation() { + this = + ModelOutput::getATypeNode("actiondispatch", "Mime::Type") + .getAMethodCall(["match?", "=~"]) + .getArgument(0) } - - override string getControllerComponent() { result = "" } - - override string toString() { result = call.toString() } - - override Location getLocation() { result = call.getLocation() } } /** - * A route block that is guarded by a conditional statement. - * For example: - * ```rb - * if Rails.env.test? - * get "/foo/bar", to: "foo#bar" - * end - * ``` - * We ignore the condition and analyze both branches to obtain as - * much routing information as possible. + * Models routing configuration specified using the `ActionDispatch` library, which is part of Rails. */ - private class ConditionalRouteBlock extends NestedRouteBlock, TConditionalRouteBlock { - private ConditionalExpr e; - - ConditionalRouteBlock() { this = TConditionalRouteBlock(parent, e) } - - override string getAPrimaryQlClass() { result = "ConditionalRouteBlock" } - - override Stmt getAStmt() { result = e.getBranch(_).(StmtSequence).getAStmt() } - - override string getPathComponent() { none() } - - override string getControllerComponent() { none() } - - override string toString() { result = e.toString() } - - override Location getLocation() { result = e.getLocation() } - } - - /** - * A route block defined by a call to `namespace`. - * ```rb - * namespace :admin do - * resources :posts - * end - * ``` - * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-namespace - */ - private class NamespaceRouteBlock extends NestedRouteBlock, TNamespaceRouteBlock { - private MethodCall call; - private Block block; - - NamespaceRouteBlock() { this = TNamespaceRouteBlock(parent, call, block) } - - override Stmt getAStmt() { result = block.getAStmt() } - - override string getPathComponent() { result = this.getNamespace() } - - override string getControllerComponent() { result = this.getNamespace() } - - private string getNamespace() { - call.getArgument(0).getConstantValue().isStringlikeValue(result) - } - - override string toString() { result = call.toString() } - - override Location getLocation() { result = call.getLocation() } - } - - /** - * A route configuration. This defines a combination of HTTP method and URL - * path which should be routed to a particular controller-action pair. - * This can arise from an explicit call to a routing method, for example: - * ```rb - * get "/photos", to: "photos#index" - * ``` - * or via a convenience method like `resources`, which defines multiple routes at once: - * ```rb - * resources :photos - * ``` - */ - class Route extends TRoute instanceof RouteImpl { + module Routing { /** - * Gets the name of a primary CodeQL class to which this route belongs. - */ - string getAPrimaryQlClass() { result = "Route" } - - /** Gets a string representation of this route. */ - string toString() { result = super.toString() } - - /** - * Gets the location of the method call that defines this route. - */ - Location getLocation() { result = super.getLocation() } - - /** - * Gets the full controller targeted by this route. - */ - string getController() { result = super.getController() } - - /** - * Gets the action targeted by this route. - */ - string getAction() { result = super.getAction() } - - /** - * Gets the HTTP method of this route. - * The result is one of [get, post, put, patch, delete]. - */ - string getHttpMethod() { result = super.getHttpMethod() } - - /** - * Gets the full path of the route. - */ - string getPath() { result = super.getPath() } - - /** - * Get a URL capture. This is a wildcard URL segment whose value is placed in `params`. - * For example, in - * ```ruby - * get "/foo/:bar/baz", to: "users#index" + * A block that defines some routes. + * Route blocks can contribute to the path or controller namespace of their child routes. + * For example, in the block below + * ```rb + * scope path: "/admin" do + * get "/dashboard", to: "admin_dashboard#show" + * end * ``` - * the capture is `:bar`. + * the route defined by the call to `get` has the full path `/admin/dashboard`. + * We track these contributions via `getPathComponent` and `getControllerComponent`. */ - string getACapture() { result = super.getACapture() } - } + abstract private class RouteBlock extends TRouteBlock { + /** + * Gets the name of a primary CodeQL class to which this route block belongs. + */ + string getAPrimaryQlClass() { result = "RouteBlock" } - /** - * The implementation of `Route`. - * This class is abstract and is thus kept private so we don't expose it to - * users. - * Extend this class to add new instances of routes. - */ - abstract private class RouteImpl extends TRoute { - /** - * Gets the name of a primary CodeQL class to which this route belongs. - */ - string getAPrimaryQlClass() { result = "RouteImpl" } + /** + * Gets a string representation of this route block. + */ + string toString() { none() } - MethodCall method; + /** + * Gets a `Stmt` within this route block. + */ + abstract Stmt getAStmt(); - /** Gets a string representation of this route. */ - string toString() { result = method.toString() } + /** + * Gets the parent of this route block, if one exists. + */ + abstract RouteBlock getParent(); + + /** + * Gets the `n`th parent of this route block. + * The zeroth parent is this block, the first parent is the direct parent of this block, etc. + */ + RouteBlock getParent(int n) { + if n = 0 then result = this else result = this.getParent().getParent(n - 1) + } + + /** + * Gets the component of the path defined by this block, if it exists. + */ + abstract string getPathComponent(); + + /** + * Gets the component of the controller namespace defined by this block, if it exists. + */ + abstract string getControllerComponent(); + + /** + * Gets the location of this route block. + */ + abstract Location getLocation(); + } /** - * Gets the location of the method call that defines this route. + * A route block that is not the top-level block. + * This block will always have a parent. */ - Location getLocation() { result = method.getLocation() } + abstract private class NestedRouteBlock extends RouteBlock { + RouteBlock parent; + + override RouteBlock getParent() { result = parent } + + override string getAPrimaryQlClass() { result = "NestedRouteBlock" } + } /** - * Gets the method call that defines this route. + * A top-level routes block. + * ```rb + * Rails.application.routes.draw do + * ... + * end + * ``` */ - MethodCall getDefiningMethodCall() { result = method } + private class TopLevelRouteBlock extends RouteBlock, TTopLevelRouteBlock { + MethodCall call; + // Routing blocks create scopes which define the namespace for controllers and paths, + // though they can be overridden in various ways. + // The namespaces can differ, so we track them separately. + Block block; + + TopLevelRouteBlock() { this = TTopLevelRouteBlock(_, call, block) } + + override string getAPrimaryQlClass() { result = "TopLevelRouteBlock" } + + Block getBlock() { result = block } + + override Stmt getAStmt() { result = block.getAStmt() } + + override RouteBlock getParent() { none() } + + override string toString() { result = call.toString() } + + override Location getLocation() { result = call.getLocation() } + + override string getPathComponent() { none() } + + override string getControllerComponent() { none() } + } /** - * Get the last component of the path. For example, in + * A route block defined by a call to `constraints`. + * ```rb + * constraints(foo: /some_regex/) do + * get "/posts/:foo", to "posts#something" + * end + * ``` + * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-constraints + */ + private class ConstraintsRouteBlock extends NestedRouteBlock, TConstraintsRouteBlock { + private Block block; + private MethodCall call; + + ConstraintsRouteBlock() { this = TConstraintsRouteBlock(parent, call, block) } + + override string getAPrimaryQlClass() { result = "ConstraintsRouteBlock" } + + override Stmt getAStmt() { result = block.getAStmt() } + + override string getPathComponent() { result = "" } + + override string getControllerComponent() { result = "" } + + override string toString() { result = call.toString() } + + override Location getLocation() { result = call.getLocation() } + } + + /** + * A route block defined by a call to `scope`. + * ```rb + * scope(path: "/some_path", module: "some_module") do + * get "/posts/:foo", to "posts#something" + * end + * ``` + * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-scope + */ + private class ScopeRouteBlock extends NestedRouteBlock, TScopeRouteBlock { + private MethodCall call; + private Block block; + + ScopeRouteBlock() { this = TScopeRouteBlock(parent, call, block) } + + override string getAPrimaryQlClass() { result = "ScopeRouteBlock" } + + override Stmt getAStmt() { result = block.getAStmt() } + + override string toString() { result = call.toString() } + + override Location getLocation() { result = call.getLocation() } + + override string getPathComponent() { + call.getKeywordArgument("path").getConstantValue().isStringlikeValue(result) + or + not exists(call.getKeywordArgument("path")) and + call.getArgument(0).getConstantValue().isStringlikeValue(result) + } + + override string getControllerComponent() { + call.getKeywordArgument(["controller", "module"]) + .getConstantValue() + .isStringlikeValue(result) + } + } + + /** + * A route block defined by a call to `resources`. + * ```rb + * resources :articles do + * get "/comments", to "comments#index" + * end + * ``` + * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Resources.html#method-i-resources + */ + private class ResourcesRouteBlock extends NestedRouteBlock, TResourcesRouteBlock { + private MethodCall call; + private Block block; + + ResourcesRouteBlock() { this = TResourcesRouteBlock(parent, call, block) } + + override string getAPrimaryQlClass() { result = "ResourcesRouteBlock" } + + override Stmt getAStmt() { result = block.getAStmt() } + + /** + * Gets the `resources` call that gives rise to this route block. + */ + MethodCall getDefiningMethodCall() { result = call } + + override string getPathComponent() { + exists(string resource | + call.getArgument(0).getConstantValue().isStringlikeValue(resource) + | + result = resource + "/:" + singularize(resource) + "_id" + ) + } + + override string getControllerComponent() { result = "" } + + override string toString() { result = call.toString() } + + override Location getLocation() { result = call.getLocation() } + } + + /** + * A route block that is guarded by a conditional statement. + * For example: + * ```rb + * if Rails.env.test? + * get "/foo/bar", to: "foo#bar" + * end + * ``` + * We ignore the condition and analyze both branches to obtain as + * much routing information as possible. + */ + private class ConditionalRouteBlock extends NestedRouteBlock, TConditionalRouteBlock { + private ConditionalExpr e; + + ConditionalRouteBlock() { this = TConditionalRouteBlock(parent, e) } + + override string getAPrimaryQlClass() { result = "ConditionalRouteBlock" } + + override Stmt getAStmt() { result = e.getBranch(_).(StmtSequence).getAStmt() } + + override string getPathComponent() { none() } + + override string getControllerComponent() { none() } + + override string toString() { result = e.toString() } + + override Location getLocation() { result = e.getLocation() } + } + + /** + * A route block defined by a call to `namespace`. + * ```rb + * namespace :admin do + * resources :posts + * end + * ``` + * https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/Scoping.html#method-i-namespace + */ + private class NamespaceRouteBlock extends NestedRouteBlock, TNamespaceRouteBlock { + private MethodCall call; + private Block block; + + NamespaceRouteBlock() { this = TNamespaceRouteBlock(parent, call, block) } + + override Stmt getAStmt() { result = block.getAStmt() } + + override string getPathComponent() { result = this.getNamespace() } + + override string getControllerComponent() { result = this.getNamespace() } + + private string getNamespace() { + call.getArgument(0).getConstantValue().isStringlikeValue(result) + } + + override string toString() { result = call.toString() } + + override Location getLocation() { result = call.getLocation() } + } + + /** + * A route configuration. This defines a combination of HTTP method and URL + * path which should be routed to a particular controller-action pair. + * This can arise from an explicit call to a routing method, for example: * ```rb * get "/photos", to: "photos#index" * ``` - * this is `/photos`. - * If the string has any interpolations, this predicate will have no result. - */ - abstract string getLastPathComponent(); - - /** - * Gets the HTTP method of this route. - * The result is one of [get, post, put, patch, delete]. - */ - abstract string getHttpMethod(); - - /** - * Gets the last controller component. - * This is the controller specified in the route itself. - */ - abstract string getLastControllerComponent(); - - /** - * Gets a component of the controller. - * This behaves identically to `getPathComponent`, but for controller information. - */ - string getControllerComponent(int n) { - if n = 0 - then result = this.getLastControllerComponent() - else result = this.getParentBlock().getParent(n - 1).getControllerComponent() - } - - /** - * Gets the full controller targeted by this route. - */ - string getController() { - result = - concat(int n | - this.getControllerComponent(n) != "" - | - this.getControllerComponent(n), "/" order by n desc - ) - } - - /** - * Gets the action targeted by this route. - */ - abstract string getAction(); - - /** - * Gets the parent `RouteBlock` of this route. - */ - abstract RouteBlock getParentBlock(); - - /** - * Gets a component of the path. Components are numbered from 0 up, where 0 - * is the last component, 1 is the second-last, etc. - * For example, in the following route: - * + * or via a convenience method like `resources`, which defines multiple routes at once: * ```rb - * namespace path: "foo" do - * namespace path: "bar" do - * get "baz", to: "foo#bar - * end - * end + * resources :photos * ``` - * - * the components are: - * - * | n | component - * |---|---------- - * | 0 | baz - * | 1 | bar - * | 2 | foo */ - string getPathComponent(int n) { - if n = 0 - then result = this.getLastPathComponent() - else result = this.getParentBlock().getParent(n - 1).getPathComponent() + class Route extends TRoute instanceof RouteImpl { + /** + * Gets the name of a primary CodeQL class to which this route belongs. + */ + string getAPrimaryQlClass() { result = "Route" } + + /** Gets a string representation of this route. */ + string toString() { result = super.toString() } + + /** + * Gets the location of the method call that defines this route. + */ + Location getLocation() { result = super.getLocation() } + + /** + * Gets the full controller targeted by this route. + */ + string getController() { result = super.getController() } + + /** + * Gets the action targeted by this route. + */ + string getAction() { result = super.getAction() } + + /** + * Gets the HTTP method of this route. + * The result is one of [get, post, put, patch, delete]. + */ + string getHttpMethod() { result = super.getHttpMethod() } + + /** + * Gets the full path of the route. + */ + string getPath() { result = super.getPath() } + + /** + * Get a URL capture. This is a wildcard URL segment whose value is placed in `params`. + * For example, in + * ```ruby + * get "/foo/:bar/baz", to: "users#index" + * ``` + * the capture is `:bar`. + */ + string getACapture() { result = super.getACapture() } } /** - * Gets the full path of the route. + * The implementation of `Route`. + * This class is abstract and is thus kept private so we don't expose it to + * users. + * Extend this class to add new instances of routes. */ - string getPath() { - result = - concat(int n | - this.getPathComponent(n) != "" - | - // Strip leading and trailing slashes from each path component before combining - stripSlashes(this.getPathComponent(n)), "/" order by n desc - ) - } + abstract private class RouteImpl extends TRoute { + /** + * Gets the name of a primary CodeQL class to which this route belongs. + */ + string getAPrimaryQlClass() { result = "RouteImpl" } - /** - * Get a URL capture. This is a wildcard URL segment whose value is placed in `params`. - * For example, in - * ```ruby - * get "/foo/:bar/baz", to: "users#index" - * ``` - * the capture is `:bar`. - * We don't currently make use of this, but it may be useful in future to more accurately - * model the contents of the `params` hash. - */ - string getACapture() { result = this.getPathComponent(_).regexpFind(":[^:/]+", _, _) } - } + MethodCall method; - /** - * A route generated by an explicit call to `get`, `post`, etc. - * - * ```ruby - * get "/photos", to: "photos#index" - * put "/photos/:id", to: "photos#update" - * ``` - */ - private class ExplicitRoute extends RouteImpl, TExplicitRoute { - RouteBlock parentBlock; + /** Gets a string representation of this route. */ + string toString() { result = method.toString() } - ExplicitRoute() { this = TExplicitRoute(parentBlock, method) } + /** + * Gets the location of the method call that defines this route. + */ + Location getLocation() { result = method.getLocation() } - override string getAPrimaryQlClass() { result = "ExplicitRoute" } + /** + * Gets the method call that defines this route. + */ + MethodCall getDefiningMethodCall() { result = method } - override RouteBlock getParentBlock() { result = parentBlock } + /** + * Get the last component of the path. For example, in + * ```rb + * get "/photos", to: "photos#index" + * ``` + * this is `/photos`. + * If the string has any interpolations, this predicate will have no result. + */ + abstract string getLastPathComponent(); - override string getLastPathComponent() { - method.getArgument(0).getConstantValue().isStringlikeValue(result) - } + /** + * Gets the HTTP method of this route. + * The result is one of [get, post, put, patch, delete]. + */ + abstract string getHttpMethod(); - override string getLastControllerComponent() { - method.getKeywordArgument("controller").getConstantValue().isStringlikeValue(result) - or - not exists(method.getKeywordArgument("controller")) and - ( - result = extractController(this.getActionString()) - or - // If controller is not specified, and we're in a `resources` route block, use the controller of that route. - // For example, in - // - // resources :posts do - // get "timestamp", to: :timestamp - // end - // - // The route is GET /posts/:post_id/timestamp => posts/timestamp - not exists(extractController(this.getActionString())) and - exists(ResourcesRoute r | - r.getDefiningMethodCall() = parentBlock.(ResourcesRouteBlock).getDefiningMethodCall() - | - result = r.getLastControllerComponent() - ) - ) - } + /** + * Gets the last controller component. + * This is the controller specified in the route itself. + */ + abstract string getLastControllerComponent(); - private string getActionString() { - method.getKeywordArgument("to").getConstantValue().isStringlikeValue(result) - or - method.getKeywordArgument("to").(MethodCall).getMethodName() = "redirect" and - result = "#" - } - - override string getAction() { - // get "/photos", action: "index" - method.getKeywordArgument("action").getConstantValue().isStringlikeValue(result) - or - not exists(method.getKeywordArgument("action")) and - ( - // get "/photos", to: "photos#index" - // get "/photos", to: redirect("some_url") - result = extractAction(this.getActionString()) - or - // resources :photos, only: [] do - // get "/", to: "index" - // end - not exists(extractAction(this.getActionString())) and result = this.getActionString() - or - // get :some_action - not exists(this.getActionString()) and - method.getArgument(0).getConstantValue().isStringlikeValue(result) - ) - } - - override string getHttpMethod() { result = method.getMethodName().toString() } - } - - /** - * A route generated by a call to `resources`. - * - * ```ruby - * resources :photos - * ``` - * This creates eight routes, equivalent to the following code: - * ```ruby - * get "/photos", to: "photos#index" - * get "/photos/new", to: "photos#new" - * post "/photos", to: "photos#create" - * get "/photos/:id", to: "photos#show" - * get "/photos/:id/edit", to: "photos#edit" - * patch "/photos/:id", to: "photos#update" - * put "/photos/:id", to: "photos#update" - * delete "/photos/:id", to: "photos#delete" - * ``` - * - * `resources` can take a block. Any routes defined inside the block will inherit a path component of - * `//:_id`. For example: - * - * ```ruby - * resources :photos do - * get "/foo", to: "photos#foo" - * end - * ``` - * This creates the eight default routes, plus one more, which is nested under "/photos/:photo_id", equivalent to: - * ```ruby - * get "/photos/:photo_id/foo", to: "photos#foo" - * ``` - */ - private class ResourcesRoute extends RouteImpl, TResourcesRoute { - RouteBlock parent; - string action; - string httpMethod; - string pathComponent; - - ResourcesRoute() { - exists(string resource | - this = TResourcesRoute(parent, method, action) and - method.getArgument(0).getConstantValue().isStringlikeValue(resource) and - isDefaultResourceRoute(resource, httpMethod, pathComponent, action) - ) - } - - override string getAPrimaryQlClass() { result = "ResourcesRoute" } - - override RouteBlock getParentBlock() { result = parent } - - override string getLastPathComponent() { result = pathComponent } - - override string getLastControllerComponent() { - method.getArgument(0).getConstantValue().isStringlikeValue(result) - } - - override string getAction() { result = action } - - override string getHttpMethod() { result = httpMethod } - } - - /** - * A route generated by a call to `resource`. - * This is like a `resources` route, but creates routes for a singular resource. - * This means there's no index route, no id parameter, and the resource name is expected to be singular. - * It will still be routed to a pluralised controller name. - * ```ruby - * resource :account - * ``` - */ - private class SingularResourceRoute extends RouteImpl, TResourceRoute { - RouteBlock parent; - string action; - string httpMethod; - string pathComponent; - - SingularResourceRoute() { - exists(string resource | - this = TResourceRoute(parent, method, action) and - method.getArgument(0).getConstantValue().isStringlikeValue(resource) and - isDefaultSingularResourceRoute(resource, httpMethod, pathComponent, action) - ) - } - - override string getAPrimaryQlClass() { result = "SingularResourceRoute" } - - override RouteBlock getParentBlock() { result = parent } - - override string getLastPathComponent() { result = pathComponent } - - override string getLastControllerComponent() { - method.getArgument(0).getConstantValue().isStringlikeValue(result) - } - - override string getAction() { result = action } - - override string getHttpMethod() { result = httpMethod } - } - - /** - * A route generated by a call to `match`. - * This is a lower level primitive that powers `get`, `post` etc. - * The first argument can be a path or a (path, controller-action) pair. - * The controller, action and HTTP method can be specified with the - * `controller:`, `action:` and `via:` keyword arguments, respectively. - * ```ruby - * match 'photos/:id' => 'photos#show', via: :get - * match 'photos/:id', to: 'photos#show', via: :get - * match 'photos/:id', to 'photos#show', via: [:get, :post] - * match 'photos/:id', controller: 'photos', action: 'show', via: :get - * ``` - */ - private class MatchRoute extends RouteImpl, TMatchRoute { - private RouteBlock parent; - - MatchRoute() { this = TMatchRoute(parent, method) } - - override string getAPrimaryQlClass() { result = "MatchRoute" } - - override RouteBlock getParentBlock() { result = parent } - - override string getLastPathComponent() { - [method.getArgument(0), method.getArgument(0).(Pair).getKey()] - .getConstantValue() - .isStringlikeValue(result) - } - - override string getLastControllerComponent() { - result = - extractController(method.getKeywordArgument("to").getConstantValue().getStringlikeValue()) or - method.getKeywordArgument("controller").getConstantValue().isStringlikeValue(result) or - result = - extractController(method - .getArgument(0) - .(Pair) - .getValue() - .getConstantValue() - .getStringlikeValue()) - } - - override string getHttpMethod() { - exists(string via | - method.getKeywordArgument("via").getConstantValue().isStringlikeValue(via) - | - via = "all" and result = anyHttpMethod() - or - via != "all" and result = via - ) - or - result = - method - .getKeywordArgument("via") - .(ArrayLiteral) - .getElement(_) - .getConstantValue() - .getStringlikeValue() - } - - override string getAction() { - result = - extractAction(method.getKeywordArgument("to").getConstantValue().getStringlikeValue()) or - method.getKeywordArgument("action").getConstantValue().isStringlikeValue(result) or - result = - extractAction(method - .getArgument(0) - .(Pair) - .getValue() - .getConstantValue() - .getStringlikeValue()) - } - } - - private import Cached - - /** - * This module contains the IPA types backing `RouteBlock` and `Route`, cached for performance. - */ - cached - private module Cached { - cached - newtype TRouteBlock = - TTopLevelRouteBlock(MethodCall routes, MethodCall draw, Block b) { - routes.getMethodName() = "routes" and - draw.getMethodName() = "draw" and - draw.getReceiver() = routes and - draw.getBlock() = b - } or - // constraints(foo: /some_regex/) do - // get "/posts/:foo", to "posts#something" - // end - TConstraintsRouteBlock(RouteBlock parent, MethodCall constraints, Block b) { - parent.getAStmt() = constraints and - constraints.getMethodName() = "constraints" and - constraints.getBlock() = b - } or - // scope(path: "/some_path", module: "some_module") do - // get "/posts/:foo", to "posts#something" - // end - TScopeRouteBlock(RouteBlock parent, MethodCall scope, Block b) { - parent.getAStmt() = scope and scope.getMethodName() = "scope" and scope.getBlock() = b - } or - // resources :articles do - // get "/comments", to "comments#index" - // end - TResourcesRouteBlock(RouteBlock parent, MethodCall resources, Block b) { - parent.getAStmt() = resources and - resources.getMethodName() = "resources" and - resources.getBlock() = b - } or - // A conditional statement guarding some routes. - // We ignore the condition and analyze both branches to obtain as - // much routing information as possible. - TConditionalRouteBlock(RouteBlock parent, ConditionalExpr e) { parent.getAStmt() = e } or - // namespace :admin do - // resources :posts - // end - TNamespaceRouteBlock(RouteBlock parent, MethodCall namespace, Block b) { - parent.getAStmt() = namespace and - namespace.getMethodName() = "namespace" and - namespace.getBlock() = b + /** + * Gets a component of the controller. + * This behaves identically to `getPathComponent`, but for controller information. + */ + string getControllerComponent(int n) { + if n = 0 + then result = this.getLastControllerComponent() + else result = this.getParentBlock().getParent(n - 1).getControllerComponent() } + /** + * Gets the full controller targeted by this route. + */ + string getController() { + result = + concat(int n | + this.getControllerComponent(n) != "" + | + this.getControllerComponent(n), "/" order by n desc + ) + } + + /** + * Gets the action targeted by this route. + */ + abstract string getAction(); + + /** + * Gets the parent `RouteBlock` of this route. + */ + abstract RouteBlock getParentBlock(); + + /** + * Gets a component of the path. Components are numbered from 0 up, where 0 + * is the last component, 1 is the second-last, etc. + * For example, in the following route: + * + * ```rb + * namespace path: "foo" do + * namespace path: "bar" do + * get "baz", to: "foo#bar + * end + * end + * ``` + * + * the components are: + * + * | n | component + * |---|---------- + * | 0 | baz + * | 1 | bar + * | 2 | foo + */ + string getPathComponent(int n) { + if n = 0 + then result = this.getLastPathComponent() + else result = this.getParentBlock().getParent(n - 1).getPathComponent() + } + + /** + * Gets the full path of the route. + */ + string getPath() { + result = + concat(int n | + this.getPathComponent(n) != "" + | + // Strip leading and trailing slashes from each path component before combining + stripSlashes(this.getPathComponent(n)), "/" order by n desc + ) + } + + /** + * Get a URL capture. This is a wildcard URL segment whose value is placed in `params`. + * For example, in + * ```ruby + * get "/foo/:bar/baz", to: "users#index" + * ``` + * the capture is `:bar`. + * We don't currently make use of this, but it may be useful in future to more accurately + * model the contents of the `params` hash. + */ + string getACapture() { result = this.getPathComponent(_).regexpFind(":[^:/]+", _, _) } + } + /** - * A route configuration. See `Route` for more info + * A route generated by an explicit call to `get`, `post`, etc. + * + * ```ruby + * get "/photos", to: "photos#index" + * put "/photos/:id", to: "photos#update" + * ``` + */ + private class ExplicitRoute extends RouteImpl, TExplicitRoute { + RouteBlock parentBlock; + + ExplicitRoute() { this = TExplicitRoute(parentBlock, method) } + + override string getAPrimaryQlClass() { result = "ExplicitRoute" } + + override RouteBlock getParentBlock() { result = parentBlock } + + override string getLastPathComponent() { + method.getArgument(0).getConstantValue().isStringlikeValue(result) + } + + override string getLastControllerComponent() { + method.getKeywordArgument("controller").getConstantValue().isStringlikeValue(result) + or + not exists(method.getKeywordArgument("controller")) and + ( + result = extractController(this.getActionString()) + or + // If controller is not specified, and we're in a `resources` route block, use the controller of that route. + // For example, in + // + // resources :posts do + // get "timestamp", to: :timestamp + // end + // + // The route is GET /posts/:post_id/timestamp => posts/timestamp + not exists(extractController(this.getActionString())) and + exists(ResourcesRoute r | + r.getDefiningMethodCall() = parentBlock.(ResourcesRouteBlock).getDefiningMethodCall() + | + result = r.getLastControllerComponent() + ) + ) + } + + private string getActionString() { + method.getKeywordArgument("to").getConstantValue().isStringlikeValue(result) + or + method.getKeywordArgument("to").(MethodCall).getMethodName() = "redirect" and + result = "#" + } + + override string getAction() { + // get "/photos", action: "index" + method.getKeywordArgument("action").getConstantValue().isStringlikeValue(result) + or + not exists(method.getKeywordArgument("action")) and + ( + // get "/photos", to: "photos#index" + // get "/photos", to: redirect("some_url") + result = extractAction(this.getActionString()) + or + // resources :photos, only: [] do + // get "/", to: "index" + // end + not exists(extractAction(this.getActionString())) and result = this.getActionString() + or + // get :some_action + not exists(this.getActionString()) and + method.getArgument(0).getConstantValue().isStringlikeValue(result) + ) + } + + override string getHttpMethod() { result = method.getMethodName().toString() } + } + + /** + * A route generated by a call to `resources`. + * + * ```ruby + * resources :photos + * ``` + * This creates eight routes, equivalent to the following code: + * ```ruby + * get "/photos", to: "photos#index" + * get "/photos/new", to: "photos#new" + * post "/photos", to: "photos#create" + * get "/photos/:id", to: "photos#show" + * get "/photos/:id/edit", to: "photos#edit" + * patch "/photos/:id", to: "photos#update" + * put "/photos/:id", to: "photos#update" + * delete "/photos/:id", to: "photos#delete" + * ``` + * + * `resources` can take a block. Any routes defined inside the block will inherit a path component of + * `//:_id`. For example: + * + * ```ruby + * resources :photos do + * get "/foo", to: "photos#foo" + * end + * ``` + * This creates the eight default routes, plus one more, which is nested under "/photos/:photo_id", equivalent to: + * ```ruby + * get "/photos/:photo_id/foo", to: "photos#foo" + * ``` + */ + private class ResourcesRoute extends RouteImpl, TResourcesRoute { + RouteBlock parent; + string action; + string httpMethod; + string pathComponent; + + ResourcesRoute() { + exists(string resource | + this = TResourcesRoute(parent, method, action) and + method.getArgument(0).getConstantValue().isStringlikeValue(resource) and + isDefaultResourceRoute(resource, httpMethod, pathComponent, action) + ) + } + + override string getAPrimaryQlClass() { result = "ResourcesRoute" } + + override RouteBlock getParentBlock() { result = parent } + + override string getLastPathComponent() { result = pathComponent } + + override string getLastControllerComponent() { + method.getArgument(0).getConstantValue().isStringlikeValue(result) + } + + override string getAction() { result = action } + + override string getHttpMethod() { result = httpMethod } + } + + /** + * A route generated by a call to `resource`. + * This is like a `resources` route, but creates routes for a singular resource. + * This means there's no index route, no id parameter, and the resource name is expected to be singular. + * It will still be routed to a pluralised controller name. + * ```ruby + * resource :account + * ``` + */ + private class SingularResourceRoute extends RouteImpl, TResourceRoute { + RouteBlock parent; + string action; + string httpMethod; + string pathComponent; + + SingularResourceRoute() { + exists(string resource | + this = TResourceRoute(parent, method, action) and + method.getArgument(0).getConstantValue().isStringlikeValue(resource) and + isDefaultSingularResourceRoute(resource, httpMethod, pathComponent, action) + ) + } + + override string getAPrimaryQlClass() { result = "SingularResourceRoute" } + + override RouteBlock getParentBlock() { result = parent } + + override string getLastPathComponent() { result = pathComponent } + + override string getLastControllerComponent() { + method.getArgument(0).getConstantValue().isStringlikeValue(result) + } + + override string getAction() { result = action } + + override string getHttpMethod() { result = httpMethod } + } + + /** + * A route generated by a call to `match`. + * This is a lower level primitive that powers `get`, `post` etc. + * The first argument can be a path or a (path, controller-action) pair. + * The controller, action and HTTP method can be specified with the + * `controller:`, `action:` and `via:` keyword arguments, respectively. + * ```ruby + * match 'photos/:id' => 'photos#show', via: :get + * match 'photos/:id', to: 'photos#show', via: :get + * match 'photos/:id', to 'photos#show', via: [:get, :post] + * match 'photos/:id', controller: 'photos', action: 'show', via: :get + * ``` + */ + private class MatchRoute extends RouteImpl, TMatchRoute { + private RouteBlock parent; + + MatchRoute() { this = TMatchRoute(parent, method) } + + override string getAPrimaryQlClass() { result = "MatchRoute" } + + override RouteBlock getParentBlock() { result = parent } + + override string getLastPathComponent() { + [method.getArgument(0), method.getArgument(0).(Pair).getKey()] + .getConstantValue() + .isStringlikeValue(result) + } + + override string getLastControllerComponent() { + result = + extractController(method.getKeywordArgument("to").getConstantValue().getStringlikeValue()) or + method.getKeywordArgument("controller").getConstantValue().isStringlikeValue(result) or + result = + extractController(method + .getArgument(0) + .(Pair) + .getValue() + .getConstantValue() + .getStringlikeValue()) + } + + override string getHttpMethod() { + exists(string via | + method.getKeywordArgument("via").getConstantValue().isStringlikeValue(via) + | + via = "all" and result = anyHttpMethod() + or + via != "all" and result = via + ) + or + result = + method + .getKeywordArgument("via") + .(ArrayLiteral) + .getElement(_) + .getConstantValue() + .getStringlikeValue() + } + + override string getAction() { + result = + extractAction(method.getKeywordArgument("to").getConstantValue().getStringlikeValue()) or + method.getKeywordArgument("action").getConstantValue().isStringlikeValue(result) or + result = + extractAction(method + .getArgument(0) + .(Pair) + .getValue() + .getConstantValue() + .getStringlikeValue()) + } + } + + private import Cached + + /** + * This module contains the IPA types backing `RouteBlock` and `Route`, cached for performance. */ cached - newtype TRoute = + private module Cached { + cached + newtype TRouteBlock = + TTopLevelRouteBlock(MethodCall routes, MethodCall draw, Block b) { + routes.getMethodName() = "routes" and + draw.getMethodName() = "draw" and + draw.getReceiver() = routes and + draw.getBlock() = b + } or + // constraints(foo: /some_regex/) do + // get "/posts/:foo", to "posts#something" + // end + TConstraintsRouteBlock(RouteBlock parent, MethodCall constraints, Block b) { + parent.getAStmt() = constraints and + constraints.getMethodName() = "constraints" and + constraints.getBlock() = b + } or + // scope(path: "/some_path", module: "some_module") do + // get "/posts/:foo", to "posts#something" + // end + TScopeRouteBlock(RouteBlock parent, MethodCall scope, Block b) { + parent.getAStmt() = scope and scope.getMethodName() = "scope" and scope.getBlock() = b + } or + // resources :articles do + // get "/comments", to "comments#index" + // end + TResourcesRouteBlock(RouteBlock parent, MethodCall resources, Block b) { + parent.getAStmt() = resources and + resources.getMethodName() = "resources" and + resources.getBlock() = b + } or + // A conditional statement guarding some routes. + // We ignore the condition and analyze both branches to obtain as + // much routing information as possible. + TConditionalRouteBlock(RouteBlock parent, ConditionalExpr e) { parent.getAStmt() = e } or + // namespace :admin do + // resources :posts + // end + TNamespaceRouteBlock(RouteBlock parent, MethodCall namespace, Block b) { + parent.getAStmt() = namespace and + namespace.getMethodName() = "namespace" and + namespace.getBlock() = b + } + /** - * See `ExplicitRoute` + * A route configuration. See `Route` for more info */ - TExplicitRoute(RouteBlock b, MethodCall m) { - b.getAStmt() = m and m.getMethodName() = anyHttpMethod() - } or - /** - * See `ResourcesRoute` - */ - TResourcesRoute(RouteBlock b, MethodCall m, string action) { - b.getAStmt() = m and - m.getMethodName() = "resources" and - action in ["show", "index", "new", "edit", "create", "update", "destroy"] and - applyActionFilters(m, action) - } or - /** - * See `SingularResourceRoute` - */ - TResourceRoute(RouteBlock b, MethodCall m, string action) { - b.getAStmt() = m and - m.getMethodName() = "resource" and - action in ["show", "new", "edit", "create", "update", "destroy"] and - applyActionFilters(m, action) - } or - /** - * See `MatchRoute` - */ - TMatchRoute(RouteBlock b, MethodCall m) { b.getAStmt() = m and m.getMethodName() = "match" } - } + cached + newtype TRoute = + /** + * See `ExplicitRoute` + */ + TExplicitRoute(RouteBlock b, MethodCall m) { + b.getAStmt() = m and m.getMethodName() = anyHttpMethod() + } or + /** + * See `ResourcesRoute` + */ + TResourcesRoute(RouteBlock b, MethodCall m, string action) { + b.getAStmt() = m and + m.getMethodName() = "resources" and + action in ["show", "index", "new", "edit", "create", "update", "destroy"] and + applyActionFilters(m, action) + } or + /** + * See `SingularResourceRoute` + */ + TResourceRoute(RouteBlock b, MethodCall m, string action) { + b.getAStmt() = m and + m.getMethodName() = "resource" and + action in ["show", "new", "edit", "create", "update", "destroy"] and + applyActionFilters(m, action) + } or + /** + * See `MatchRoute` + */ + TMatchRoute(RouteBlock b, MethodCall m) { b.getAStmt() = m and m.getMethodName() = "match" } + } - /** - * Several routing methods support the keyword arguments `only:` and `except:`. - * - `only:` restricts the set of actions to just those in the argument. - * - `except:` removes the given actions from the set. - */ - bindingset[action] - private predicate applyActionFilters(MethodCall m, string action) { - // Respect the `only` keyword argument, which restricts the set of actions. - ( - not exists(m.getKeywordArgument("only")) - or - exists(Expr only | only = m.getKeywordArgument("only") | - [only.(ArrayLiteral).getElement(_), only].getConstantValue().isStringlikeValue(action) - ) - ) and - // Respect the `except` keyword argument, which removes actions from the default set. - ( - not exists(m.getKeywordArgument("except")) - or - exists(Expr except | except = m.getKeywordArgument("except") | - [except.(ArrayLiteral).getElement(_), except].getConstantValue().getStringlikeValue() != - action - ) - ) - } - - /** - * Holds if the (resource, method, path, action) combination would be generated by a call to `resources :`. - */ - bindingset[resource] - private predicate isDefaultResourceRoute( - string resource, string method, string path, string action - ) { - action = "create" and - (method = "post" and path = "/" + resource) - or - action = "index" and - (method = "get" and path = "/" + resource) - or - action = "new" and - (method = "get" and path = "/" + resource + "/new") - or - action = "edit" and - (method = "get" and path = "/" + resource + ":id/edit") - or - action = "show" and - (method = "get" and path = "/" + resource + "/:id") - or - action = "update" and - (method in ["put", "patch"] and path = "/" + resource + "/:id") - or - action = "destroy" and - (method = "delete" and path = "/" + resource + "/:id") - } - - /** - * Holds if the (resource, method, path, action) combination would be generated by a call to `resource :`. - */ - bindingset[resource] - private predicate isDefaultSingularResourceRoute( - string resource, string method, string path, string action - ) { - action = "create" and - (method = "post" and path = "/" + resource) - or - action = "new" and - (method = "get" and path = "/" + resource + "/new") - or - action = "edit" and - (method = "get" and path = "/" + resource + "/edit") - or - action = "show" and - (method = "get" and path = "/" + resource) - or - action = "update" and - (method in ["put", "patch"] and path = "/" + resource) - or - action = "destroy" and - (method = "delete" and path = "/" + resource) - } - - /** - * Extract the controller from a Rails routing string - * ``` - * extractController("posts#show") = "posts" - * ``` - */ - bindingset[input] - private string extractController(string input) { result = input.regexpCapture("([^#]+)#.+", 1) } - - /** - * Extract the action from a Rails routing string - * ``` - * extractAction("posts#show") = "show" - */ - bindingset[input] - private string extractAction(string input) { result = input.regexpCapture("[^#]+#(.+)", 1) } - - /** - * Returns the lowercase name of every HTTP method we support. - */ - private string anyHttpMethod() { result = ["get", "post", "put", "patch", "delete"] } - - /** - * The inverse of `pluralize` - * photos => photo - * stories => story - * not_plural => not_plural - */ - bindingset[input] - private string singularize(string input) { - exists(string prefix | prefix = input.regexpCapture("(.*)ies", 1) | result = prefix + "y") - or - not input.matches("%ies") and - exists(string prefix | prefix = input.regexpCapture("(.*)s", 1) | result = prefix) - or - not input.regexpMatch(".*(ies|s)") and result = input - } - - /** - * Convert a camel-case string to underscore case. Converts `::` to `/`. - * This can be used to convert ActiveRecord controller names to a canonical form that matches the routes they handle. - * Note: All-uppercase words like `CONSTANT` are not handled correctly. - */ - bindingset[base] - string underscore(string base) { - base = "" and result = "" - or - result = - base.charAt(0).toLowerCase() + - // Walk along the string, keeping track of the previous character - // in order to determine if we've crossed a boundary. - // Boundaries are: - // - lower case to upper case: B in FooBar - // - entering a namespace: B in Foo::Bar - concat(int i, string prev, string char | - prev = base.charAt(i) and - char = base.charAt(i + 1) - | - any(string s | - char.regexpMatch("[A-Za-z0-9]") and - if prev = ":" - then s = "/" + char.toLowerCase() - else - if prev.isLowercase() and char.isUppercase() - then s = "_" + char.toLowerCase() - else s = char.toLowerCase() - ) - order by - i + /** + * Several routing methods support the keyword arguments `only:` and `except:`. + * - `only:` restricts the set of actions to just those in the argument. + * - `except:` removes the given actions from the set. + */ + bindingset[action] + private predicate applyActionFilters(MethodCall m, string action) { + // Respect the `only` keyword argument, which restricts the set of actions. + ( + not exists(m.getKeywordArgument("only")) + or + exists(Expr only | only = m.getKeywordArgument("only") | + [only.(ArrayLiteral).getElement(_), only].getConstantValue().isStringlikeValue(action) ) - } + ) and + // Respect the `except` keyword argument, which removes actions from the default set. + ( + not exists(m.getKeywordArgument("except")) + or + exists(Expr except | except = m.getKeywordArgument("except") | + [except.(ArrayLiteral).getElement(_), except].getConstantValue().getStringlikeValue() != + action + ) + ) + } - /** - * Strip leading and trailing forward slashes from the string. - */ - bindingset[input] - private string stripSlashes(string input) { - result = input.regexpReplaceAll("^/+(.+)$", "$1").regexpReplaceAll("^(.*[^/])/+$", "$1") + /** + * Holds if the (resource, method, path, action) combination would be generated by a call to `resources :`. + */ + bindingset[resource] + private predicate isDefaultResourceRoute( + string resource, string method, string path, string action + ) { + action = "create" and + (method = "post" and path = "/" + resource) + or + action = "index" and + (method = "get" and path = "/" + resource) + or + action = "new" and + (method = "get" and path = "/" + resource + "/new") + or + action = "edit" and + (method = "get" and path = "/" + resource + ":id/edit") + or + action = "show" and + (method = "get" and path = "/" + resource + "/:id") + or + action = "update" and + (method in ["put", "patch"] and path = "/" + resource + "/:id") + or + action = "destroy" and + (method = "delete" and path = "/" + resource + "/:id") + } + + /** + * Holds if the (resource, method, path, action) combination would be generated by a call to `resource :`. + */ + bindingset[resource] + private predicate isDefaultSingularResourceRoute( + string resource, string method, string path, string action + ) { + action = "create" and + (method = "post" and path = "/" + resource) + or + action = "new" and + (method = "get" and path = "/" + resource + "/new") + or + action = "edit" and + (method = "get" and path = "/" + resource + "/edit") + or + action = "show" and + (method = "get" and path = "/" + resource) + or + action = "update" and + (method in ["put", "patch"] and path = "/" + resource) + or + action = "destroy" and + (method = "delete" and path = "/" + resource) + } + + /** + * Extract the controller from a Rails routing string + * ``` + * extractController("posts#show") = "posts" + * ``` + */ + bindingset[input] + private string extractController(string input) { result = input.regexpCapture("([^#]+)#.+", 1) } + + /** + * Extract the action from a Rails routing string + * ``` + * extractAction("posts#show") = "show" + */ + bindingset[input] + private string extractAction(string input) { result = input.regexpCapture("[^#]+#(.+)", 1) } + + /** + * Returns the lowercase name of every HTTP method we support. + */ + private string anyHttpMethod() { result = ["get", "post", "put", "patch", "delete"] } + + /** + * The inverse of `pluralize`. If `input` is a plural word, it returns the + * singular version. + * + * Examples: + * + * - photos -> photo + * - stories -> story + * - not_plural -> not_plural + */ + bindingset[input] + private string singularize(string input) { + exists(string prefix | prefix = input.regexpCapture("(.*)ies", 1) | result = prefix + "y") + or + not input.matches("%ies") and + exists(string prefix | prefix = input.regexpCapture("(.*)s", 1) | result = prefix) + or + not input.regexpMatch(".*(ies|s)") and result = input + } + + /** + * Convert a camel-case string to underscore case. Converts `::` to `/`. + * This can be used to convert ActiveRecord controller names to a canonical form that matches the routes they handle. + * Note: All-uppercase words like `CONSTANT` are not handled correctly. + */ + bindingset[base] + string underscore(string base) { + base = "" and result = "" + or + result = + base.charAt(0).toLowerCase() + + // Walk along the string, keeping track of the previous character + // in order to determine if we've crossed a boundary. + // Boundaries are: + // - lower case to upper case: B in FooBar + // - entering a namespace: B in Foo::Bar + concat(int i, string prev, string char | + prev = base.charAt(i) and + char = base.charAt(i + 1) + | + any(string s | + char.regexpMatch("[A-Za-z0-9]") and + if prev = ":" + then s = "/" + char.toLowerCase() + else + if prev.isLowercase() and char.isUppercase() + then s = "_" + char.toLowerCase() + else s = char.toLowerCase() + ) + order by + i + ) + } + + /** + * Strip leading and trailing forward slashes from the string. + */ + bindingset[input] + private string stripSlashes(string input) { + result = input.regexpReplaceAll("^/+(.+)$", "$1").regexpReplaceAll("^(.*[^/])/+$", "$1") + } } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll index cc63e64a083..a4994118b5c 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -133,6 +133,11 @@ private Expr sqlFragmentArgument(MethodCall call) { or methodName = "reload" and result = call.getKeywordArgument("lock") + or + // Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to + // SQLi if user supplied input is passed in as an argument. + methodName = "annotate" and + result = call.getArgument(_) ) ) } @@ -359,15 +364,6 @@ private module Persistence { ) } - /** - * Holds if `call` has a keyword argument with value `value`. - */ - private predicate keywordArgumentWithValue(DataFlow::CallNode call, DataFlow::ExprNode value) { - exists(ExprNodes::PairCfgNode pair | pair = call.getArgument(_).asExpr() | - value.asExpr() = pair.getValue() - ) - } - /** A call to e.g. `User.create(name: "foo")` */ private class CreateLikeCall extends DataFlow::CallNode, PersistentWriteAccess::Range { CreateLikeCall() { @@ -381,8 +377,12 @@ private module Persistence { override DataFlow::Node getValue() { // attrs as hash elements in arg0 - hashArgumentWithValue(this, 0, result) or - keywordArgumentWithValue(this, result) + hashArgumentWithValue(this, 0, result) + or + result = this.getKeywordArgument(_) + or + result = this.getPositionalArgument(0) and + not result.asExpr() instanceof ExprNodes::HashLiteralCfgNode } } @@ -394,11 +394,19 @@ private module Persistence { } override DataFlow::Node getValue() { - keywordArgumentWithValue(this, result) + // User.update(1, name: "foo") + result = this.getKeywordArgument(_) + or + // User.update(1, params) + exists(int n | n > 0 | + result = this.getPositionalArgument(n) and + not result.asExpr() instanceof ExprNodes::ArrayLiteralCfgNode + ) or // Case where 2 array args are passed - the first an array of IDs, and the // second an array of hashes - each hash corresponding to an ID in the // first array. + // User.update([1,2,3], [{name: "foo"}, {name: "bar"}]) exists(ExprNodes::ArrayLiteralCfgNode hashesArray | this.getArgument(0).asExpr() instanceof ExprNodes::ArrayLiteralCfgNode and hashesArray = this.getArgument(1).asExpr() @@ -467,8 +475,12 @@ private module Persistence { // attrs as hash elements in arg0 hashArgumentWithValue(this, 0, result) or + // attrs as variable in arg0 + result = this.getPositionalArgument(0) and + not result.asExpr() instanceof ExprNodes::HashLiteralCfgNode + or // keyword arg - keywordArgumentWithValue(this, result) + result = this.getKeywordArgument(_) } } @@ -504,3 +516,177 @@ private module Persistence { override DataFlow::Node getValue() { assignNode.getRhs() = result.asExpr() } } } + +/** + * A method call inside an ActiveRecord model class that establishes an + * association between this model and another model. + * + * ```rb + * class User + * has_many :posts + * has_one :profile + * end + * ``` + */ +private class ActiveRecordAssociation extends DataFlow::CallNode { + private ActiveRecordModelClass modelClass; + + ActiveRecordAssociation() { + not exists(this.asExpr().getExpr().getEnclosingMethod()) and + this.asExpr().getExpr().getEnclosingModule() = modelClass and + this.getMethodName() = ["has_one", "has_many", "belongs_to", "has_and_belongs_to_many"] + } + + /** + * Gets the class which declares this association. + * For example, in + * ```rb + * class User + * has_many :posts + * end + * ``` + * the source class is `User`. + */ + ActiveRecordModelClass getSourceClass() { result = modelClass } + + /** + * Gets the class which this association refers to. + * For example, in + * ```rb + * class User + * has_many :posts + * end + * ``` + * the target class is `Post`. + */ + ActiveRecordModelClass getTargetClass() { + result.getName().toLowerCase() = this.getTargetModelName() + } + + /** + * Gets the (lowercase) name of the model this association targets. + * For example, in `has_many :posts`, this is `post`. + */ + string getTargetModelName() { + exists(string s | + s = this.getArgument(0).asExpr().getExpr().getConstantValue().getStringlikeValue() + | + // has_one :profile + // belongs_to :user + this.isSingular() and + result = s + or + // has_many :posts + // has_many :stories + this.isCollection() and + pluralize(result) = s + ) + } + + /** Holds if this association is one-to-one */ + predicate isSingular() { this.getMethodName() = ["has_one", "belongs_to"] } + + /** Holds if this association is one-to-many or many-to-many */ + predicate isCollection() { this.getMethodName() = ["has_many", "has_and_belongs_to_many"] } +} + +/** + * Converts `input` to plural form. + */ +bindingset[input] +bindingset[result] +private string pluralize(string input) { + exists(string stem | stem + "y" = input | result = stem + "ies") + or + result = input + "s" +} + +/** + * A call to a method generated by an ActiveRecord association. + * These yield ActiveRecord collection proxies, which act like collections but + * add some additional methods. + * We exclude `_changed?` and `_previously_changed?` because these + * do not yield ActiveRecord instances. + * https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html + */ +private class ActiveRecordAssociationMethodCall extends DataFlow::CallNode { + ActiveRecordAssociation assoc; + + ActiveRecordAssociationMethodCall() { + exists(string model | model = assoc.getTargetModelName() | + this.getReceiver().(ActiveRecordInstance).getClass() = assoc.getSourceClass() and + ( + assoc.isCollection() and + ( + this.getMethodName() = pluralize(model) + ["", "="] + or + this.getMethodName() = "<<" + or + this.getMethodName() = model + ["_ids", "_ids="] + ) + or + assoc.isSingular() and + ( + this.getMethodName() = model + ["", "="] or + this.getMethodName() = ["build_", "reload_"] + model or + this.getMethodName() = "create_" + model + ["!", ""] + ) + ) + ) + } + + ActiveRecordAssociation getAssociation() { result = assoc } +} + +/** + * A method call on an ActiveRecord collection proxy that yields one or more + * ActiveRecord instances. + * Example: + * ```rb + * class User < ActiveRecord::Base + * has_many :posts + * end + * + * User.new.posts.create + * ``` + * https://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html + */ +private class ActiveRecordCollectionProxyMethodCall extends DataFlow::CallNode { + ActiveRecordCollectionProxyMethodCall() { + this.getMethodName() = + [ + "push", "concat", "build", "create", "create!", "delete", "delete_all", "destroy", + "destroy_all", "find", "distinct", "reset", "reload" + ] and + ( + this.getReceiver().(ActiveRecordAssociationMethodCall).getAssociation().isCollection() + or + exists(ActiveRecordCollectionProxyMethodCall receiver | receiver = this.getReceiver() | + receiver.getAssociation().isCollection() and + receiver.getMethodName() = ["reset", "reload", "distinct"] + ) + ) + } + + ActiveRecordAssociation getAssociation() { + result = this.getReceiver().(ActiveRecordAssociationMethodCall).getAssociation() + } +} + +/** + * A call to an association method which yields ActiveRecord instances. + */ +private class ActiveRecordAssociationModelInstantiation extends ActiveRecordModelInstantiation instanceof ActiveRecordAssociationMethodCall { + override ActiveRecordModelClass getClass() { + result = this.(ActiveRecordAssociationMethodCall).getAssociation().getTargetClass() + } +} + +/** + * A call to a method on a collection proxy which yields ActiveRecord instances. + */ +private class ActiveRecordCollectionProxyModelInstantiation extends ActiveRecordModelInstantiation instanceof ActiveRecordCollectionProxyMethodCall { + override ActiveRecordModelClass getClass() { + result = this.(ActiveRecordCollectionProxyMethodCall).getAssociation().getTargetClass() + } +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll b/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll new file mode 100644 index 00000000000..9fa17f4e5a5 --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/Arel.qll @@ -0,0 +1,31 @@ +/** + * Provides modeling for Arel, a low level SQL library that powers ActiveRecord. + * Version: 7.0.3 + * https://api.rubyonrails.org/classes/Arel.html + */ + +private import codeql.ruby.ApiGraphs +private import codeql.ruby.dataflow.FlowSummary + +/** + * Provides modeling for Arel, a low level SQL library that powers ActiveRecord. + * Version: 7.0.3 + * https://api.rubyonrails.org/classes/Arel.html + */ +module Arel { + /** + * Flow summary for `Arel.sql`. This method wraps a SQL string, marking it as + * safe. + */ + private class SqlSummary extends SummarizedCallable { + SqlSummary() { this = "Arel.sql" } + + override MethodCall getACall() { + result = API::getTopLevelMember("Arel").getAMethodCall("sql").asExpr().getExpr() + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[0]" and output = "ReturnValue" and preservesValue = false + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll index 3dacd89b25e..ede99069213 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll @@ -379,7 +379,7 @@ class GraphqlFieldResolutionMethod extends Method, HTTP::Server::RequestHandler: result.(KeywordParameter).hasName(argDefn.getArgumentName()) or // TODO this will cause false positives because now *anything* in the **args - // param will be flagged as as RoutedParameter/RemoteFlowSource, but really + // param will be flagged as RoutedParameter/RemoteFlowSource, but really // only the hash keys corresponding to the defined arguments are user input // others could be things defined in the `:extras` keyword argument to the `argument` result instanceof HashSplatParameter // often you see `def field(**args)` diff --git a/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll b/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll index 6a27018fcf5..6c4d2ab1a47 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/PosixSpawn.qll @@ -62,9 +62,9 @@ module PosixSpawn { // is shell interpreted unless there is another argument with a string // constant value. override predicate isShellInterpreted(DataFlow::Node arg) { + this.argument(arg) and not exists(DataFlow::Node otherArg | otherArg != arg and - this.argument(arg) and this.argument(otherArg) and otherArg.asExpr().getConstantValue().isString(_) ) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll b/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll index 4a61eb1dd20..807cc9f9c51 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Railties.qll @@ -43,7 +43,7 @@ module Railties { override DataFlow::Node getAnArgument() { result = this.getArgument([0, 1]) } - override predicate isShellInterpreted(DataFlow::Node arg) { any() } + override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getAnArgument() } } /** @@ -57,6 +57,6 @@ module Railties { override DataFlow::Node getAnArgument() { result = this.getArgument(0) } - override predicate isShellInterpreted(DataFlow::Node arg) { any() } + override predicate isShellInterpreted(DataFlow::Node arg) { arg = this.getAnArgument() } } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Stdlib.qll b/ruby/ql/lib/codeql/ruby/frameworks/Stdlib.qll index 11c993b1170..f735f9daf8b 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Stdlib.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Stdlib.qll @@ -4,3 +4,4 @@ import stdlib.Open3 import stdlib.Logger +import stdlib.Pathname diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll index 87733c0af9b..cf0872e770e 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Hash.qll @@ -16,65 +16,44 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch * in `Array.qll`. */ module Hash { - // cannot use API graphs due to negative recursion - private predicate isHashLiteralPair(Pair pair, ConstantValue key) { - key = DataFlow::Content::getKnownElementIndex(pair.getKey()) and - pair = any(MethodCall mc | mc.getMethodName() = "[]").getAnArgument() - } - - private class HashLiteralSymbolSummary extends SummarizedCallable { - private ConstantValue::ConstantSymbolValue symbol; - - HashLiteralSymbolSummary() { - isHashLiteralPair(_, symbol) and - this = "Hash.[" + symbol.serialize() + "]" - } - - final override MethodCall getACall() { - result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr() and - exists(result.getKeywordArgument(symbol.getSymbol())) - } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - // { symbol: x } - input = "Argument[" + symbol.getSymbol() + ":]" and - output = "ReturnValue.Element[" + symbol.serialize() + "]" and - preservesValue = true - } - } - - private class HashLiteralNonSymbolSummary extends SummarizedCallable { - private ConstantValue key; - - HashLiteralNonSymbolSummary() { - this = "Hash.[]" and - isHashLiteralPair(_, key) and + /** + * Holds if `key` is used as the non-symbol key in a hash literal. For example + * + * ```rb + * { + * :a => 1, # symbol + * "b" => 2 # non-symbol, "b" is the key + * } + * ``` + */ + private predicate isHashLiteralNonSymbolKey(ConstantValue key) { + exists(Pair pair | + key = DataFlow::Content::getKnownElementIndex(pair.getKey()) and + // cannot use API graphs due to negative recursion + pair = any(MethodCall mc | mc.getMethodName() = "[]").getAnArgument() and not key.isSymbol(_) - } + ) + } + + private class HashLiteralSummary extends SummarizedCallable { + HashLiteralSummary() { this = "Hash.[]" } final override MethodCall getACall() { - result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr() and - isHashLiteralPair(result.getAnArgument(), key) + result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr() } override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { // { 'nonsymbol' => x } - input = "Argument[0..].PairValue[" + key.serialize() + "]" and - output = "ReturnValue.Element[" + key.serialize() + "]" and - preservesValue = true - } - } - - private class HashLiteralHashSplatSummary extends SummarizedCallable { - HashLiteralHashSplatSummary() { this = "Hash.[**]" } - - final override MethodCall getACall() { - result = API::getTopLevelMember("Hash").getAMethodCall("[]").getExprNode().getExpr() and - result.getAnArgument() instanceof HashSplatExpr - } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - // { **hash } + exists(ConstantValue key | + isHashLiteralNonSymbolKey(key) and + input = "Argument[0..].PairValue[" + key.serialize() + "]" and + output = "ReturnValue.Element[" + key.serialize() + "]" and + preservesValue = true + ) + or + // { symbol: x } + // we make use of the special `hash-splat` argument kind, which contains all keyword + // arguments wrapped in an implicit hash, as well as explicit hash splat arguments input = "Argument[hash-splat].WithElement[any]" and output = "ReturnValue" and preservesValue = true diff --git a/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Pathname.qll b/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Pathname.qll new file mode 100644 index 00000000000..b6381c448ec --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/frameworks/stdlib/Pathname.qll @@ -0,0 +1,188 @@ +/** Modeling of the `Pathname` class from the Ruby standard library. */ + +private import codeql.ruby.AST +private import codeql.ruby.ApiGraphs +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.dataflow.FlowSummary +private import codeql.ruby.dataflow.internal.DataFlowDispatch +private import codeql.ruby.frameworks.data.ModelsAsData + +/** + * Modeling of the `Pathname` class from the Ruby standard library. + * + * https://docs.ruby-lang.org/en/3.1/Pathname.html + */ +module Pathname { + /** + * An instance of the `Pathname` class. For example, in + * + * ```rb + * pn = Pathname.new "foo.txt'" + * puts pn.read + * ``` + * + * there are three `PathnameInstance`s - the call to `Pathname.new`, the + * assignment `pn = ...`, and the read access to `pn` on the second line. + * + * Every `PathnameInstance` is considered to be a `FileNameSource`. + */ + class PathnameInstance extends FileNameSource, DataFlow::Node { + PathnameInstance() { this = pathnameInstance() } + } + + private DataFlow::Node pathnameInstance() { + // A call to `Pathname.new`. + result = API::getTopLevelMember("Pathname").getAnInstantiation() + or + // Class methods on `Pathname` that return a new `Pathname`. + result = API::getTopLevelMember("Pathname").getAMethodCall(["getwd", "pwd",]) + or + // Instance methods on `Pathname` that return a new `Pathname`. + exists(DataFlow::CallNode c | result = c | + c.getReceiver() = pathnameInstance() and + c.getMethodName() = + [ + "+", "/", "basename", "cleanpath", "expand_path", "join", "realpath", + "relative_path_from", "sub", "sub_ext", "to_path" + ] + ) + or + exists(DataFlow::Node inst | + inst = pathnameInstance() and + inst.(DataFlow::LocalSourceNode).flowsTo(result) + ) + } + + /** A call where the receiver is a `Pathname`. */ + class PathnameCall extends DataFlow::CallNode { + PathnameCall() { this.getReceiver() instanceof PathnameInstance } + } + + /** + * A call to `Pathname#open` or `Pathname#opendir`, considered as a + * `FileSystemAccess`. + */ + class PathnameOpen extends FileSystemAccess::Range, PathnameCall { + PathnameOpen() { this.getMethodName() = ["open", "opendir"] } + + override DataFlow::Node getAPathArgument() { result = this.getReceiver() } + } + + /** A call to `Pathname#read`, considered as a `FileSystemReadAccess`. */ + class PathnameRead extends FileSystemReadAccess::Range, PathnameCall { + PathnameRead() { this.getMethodName() = "read" } + + // The path is the receiver (the `Pathname` object). + override DataFlow::Node getAPathArgument() { result = this.getReceiver() } + + // The read data is the return value of the call. + override DataFlow::Node getADataNode() { result = this } + } + + /** A call to `Pathname#write`, considered as a `FileSystemWriteAccess`. */ + class PathnameWrite extends FileSystemWriteAccess::Range, PathnameCall { + PathnameWrite() { this.getMethodName() = "write" } + + // The path is the receiver (the `Pathname` object). + override DataFlow::Node getAPathArgument() { result = this.getReceiver() } + + // The data to write is the 0th argument. + override DataFlow::Node getADataNode() { result = this.getArgument(0) } + } + + /** A call to `Pathname#to_s`, considered as a `FileNameSource`. */ + class PathnameToSFilenameSource extends FileNameSource, PathnameCall { + PathnameToSFilenameSource() { this.getMethodName() = "to_s" } + } + + private class PathnamePermissionModification extends FileSystemPermissionModification::Range, + PathnameCall { + private DataFlow::Node permissionArg; + + PathnamePermissionModification() { + exists(string methodName | this.getMethodName() = methodName | + methodName = ["chmod", "mkdir"] and permissionArg = this.getArgument(0) + or + methodName = "mkpath" and permissionArg = this.getKeywordArgument("mode") + or + methodName = "open" and permissionArg = this.getArgument(1) + // TODO: defaults for optional args? This may depend on the umask + ) + } + + override DataFlow::Node getAPermissionNode() { result = permissionArg } + } + + /** + * Type summaries for the `Pathname` class, i.e. method calls that produce new + * `Pathname` instances. + */ + private class PathnameTypeSummary extends ModelInput::TypeModelCsv { + override predicate row(string row) { + // package1;type1;package2;type2;path + row = + [ + // Pathname.new : Pathname + ";Pathname;;;Member[Pathname].Instance", + // Pathname#+(path) : Pathname + ";Pathname;;Pathname;Method[+].ReturnValue", + // Pathname#/(path) : Pathname + ";Pathname;;Pathname;Method[/].ReturnValue", + // Pathname#basename(path) : Pathname + ";Pathname;;Pathname;Method[basename].ReturnValue", + // Pathname#cleanpath(path) : Pathname + ";Pathname;;Pathname;Method[cleanpath].ReturnValue", + // Pathname#expand_path(path) : Pathname + ";Pathname;;Pathname;Method[expand_path].ReturnValue", + // Pathname#join(path) : Pathname + ";Pathname;;Pathname;Method[join].ReturnValue", + // Pathname#realpath(path) : Pathname + ";Pathname;;Pathname;Method[realpath].ReturnValue", + // Pathname#relative_path_from(path) : Pathname + ";Pathname;;Pathname;Method[relative_path_from].ReturnValue", + // Pathname#sub(path) : Pathname + ";Pathname;;Pathname;Method[sub].ReturnValue", + // Pathname#sub_ext(path) : Pathname + ";Pathname;;Pathname;Method[sub_ext].ReturnValue", + // Pathname#to_path(path) : Pathname + ";Pathname;;Pathname;Method[to_path].ReturnValue", + ] + } + } + + /** Taint flow summaries for the `Pathname` class. */ + private class PathnameTaintSummary extends ModelInput::SummaryModelCsv { + override predicate row(string row) { + row = + [ + // Pathname.new(path) + ";;Member[Pathname].Method[new];Argument[0];ReturnValue;taint", + // Pathname#dirname + ";Pathname;Method[dirname];Argument[self];ReturnValue;taint", + // Pathname#each_filename + ";Pathname;Method[each_filename];Argument[self];Argument[block].Parameter[0];taint", + // Pathname#expand_path + ";Pathname;Method[expand_path];Argument[self];ReturnValue;taint", + // Pathname#join + ";Pathname;Method[join];Argument[self,any];ReturnValue;taint", + // Pathname#parent + ";Pathname;Method[parent];Argument[self];ReturnValue;taint", + // Pathname#realpath + ";Pathname;Method[realpath];Argument[self];ReturnValue;taint", + // Pathname#relative_path_from + ";Pathname;Method[relative_path_from];Argument[self];ReturnValue;taint", + // Pathname#to_path + ";Pathname;Method[to_path];Argument[self];ReturnValue;taint", + // Pathname#basename + ";Pathname;Method[basename];Argument[self];ReturnValue;taint", + // Pathname#cleanpath + ";Pathname;Method[cleanpath];Argument[self];ReturnValue;taint", + // Pathname#sub + ";Pathname;Method[sub];Argument[self];ReturnValue;taint", + // Pathname#sub_ext + ";Pathname;Method[sub_ext];Argument[self];ReturnValue;taint", + ] + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/printAst.qll b/ruby/ql/lib/codeql/ruby/printAst.qll index 3056e9aa49f..28f5def4969 100644 --- a/ruby/ql/lib/codeql/ruby/printAst.qll +++ b/ruby/ql/lib/codeql/ruby/printAst.qll @@ -9,6 +9,7 @@ private import AST private import codeql.ruby.Regexp as RE private import codeql.ruby.ast.internal.Synthesis +private import ast.internal.AST /** * The query can extend this class to control which nodes are printed. @@ -35,6 +36,8 @@ private predicate shouldPrintAstEdge(AstNode parent, string edgeName, AstNode ch any(PrintAstConfiguration config).shouldPrintAstEdge(parent, edgeName, child) } +private int nonSynthIndex() { result = min([-1, any(int i | exists(getSynthChild(_, i)))]) - 1 } + newtype TPrintNode = TPrintRegularAstNode(AstNode n) { shouldPrintNode(n) } or TPrintRegExpNode(RE::RegExpTerm term) { @@ -112,13 +115,22 @@ class PrintRegularAstNode extends PrintAstNode, TPrintRegularAstNode { ) } + private int getSynthAstNodeIndex() { + not astNode.isSynthesized() and result = nonSynthIndex() + or + astNode = getSynthChild(astNode.getParent(), result) + } + override int getOrder() { this = rank[result](PrintRegularAstNode p, Location l, File f | l = p.getLocation() and f = l.getFile() | - p order by f.getBaseName(), f.getAbsolutePath(), l.getStartLine(), l.getStartColumn() + p + order by + f.getBaseName(), f.getAbsolutePath(), l.getStartLine(), l.getStartColumn(), + l.getEndLine(), l.getEndColumn(), p.getSynthAstNodeIndex() ) } diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index 062d906c9a2..5a763d9c3dd 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-all -version: 0.3.1-dev +version: 0.3.3-dev groups: ruby extractor: ruby dbscheme: ruby.dbscheme diff --git a/ruby/ql/src/CHANGELOG.md b/ruby/ql/src/CHANGELOG.md index 0905f133d16..9aeec45dc3f 100644 --- a/ruby/ql/src/CHANGELOG.md +++ b/ruby/ql/src/CHANGELOG.md @@ -1,3 +1,16 @@ +## 0.3.1 + +### New Queries + +* Added a new experimental query, `rb/manually-checking-http-verb`, to detect cases when the HTTP verb for an incoming request is checked and then used as part of control flow. +* Added a new experimental query, `rb/weak-params`, to detect cases when the rails strong parameters pattern isn't followed and values flow into persistent store writes. + +## 0.3.0 + +### Breaking Changes + +* Contextual queries and the query libraries they depend on have been moved to the `codeql/ruby-all` package. + ## 0.2.0 ### New Queries diff --git a/ruby/ql/src/change-notes/2022-06-29-move-contextual-queries.md b/ruby/ql/src/change-notes/released/0.3.0.md similarity index 77% rename from ruby/ql/src/change-notes/2022-06-29-move-contextual-queries.md rename to ruby/ql/src/change-notes/released/0.3.0.md index bc85eb9361e..717a3a27aa5 100644 --- a/ruby/ql/src/change-notes/2022-06-29-move-contextual-queries.md +++ b/ruby/ql/src/change-notes/released/0.3.0.md @@ -1,4 +1,5 @@ ---- -category: breaking ---- +## 0.3.0 + +### Breaking Changes + * Contextual queries and the query libraries they depend on have been moved to the `codeql/ruby-all` package. diff --git a/ruby/ql/src/change-notes/released/0.3.1.md b/ruby/ql/src/change-notes/released/0.3.1.md new file mode 100644 index 00000000000..a95409eabd1 --- /dev/null +++ b/ruby/ql/src/change-notes/released/0.3.1.md @@ -0,0 +1,6 @@ +## 0.3.1 + +### New Queries + +* Added a new experimental query, `rb/manually-checking-http-verb`, to detect cases when the HTTP verb for an incoming request is checked and then used as part of control flow. +* Added a new experimental query, `rb/weak-params`, to detect cases when the rails strong parameters pattern isn't followed and values flow into persistent store writes. diff --git a/ruby/ql/src/codeql-pack.release.yml b/ruby/ql/src/codeql-pack.release.yml index 5274e27ed52..bb106b1cb63 100644 --- a/ruby/ql/src/codeql-pack.release.yml +++ b/ruby/ql/src/codeql-pack.release.yml @@ -1,2 +1,2 @@ --- -lastReleaseVersion: 0.2.0 +lastReleaseVersion: 0.3.1 diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp new file mode 100644 index 00000000000..d50c7f0bf30 --- /dev/null +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qhelp @@ -0,0 +1,23 @@ + + + +

    + Manually checking the HTTP request verb inside of a controller method can lead to + CSRF bypass if GET or HEAD requests are handled improperly. +

    +
    + +

    + It is better to use different controller methods for each resource/http verb combination + and configure the Rails routes in your application to call them accordingly. +

    +
    + + +
  • + See https://guides.rubyonrails.org/routing.html for more information. +
  • +
    +
    diff --git a/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql new file mode 100644 index 00000000000..2ddf7fe87b3 --- /dev/null +++ b/ruby/ql/src/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql @@ -0,0 +1,96 @@ +/** + * @name Manually checking http verb instead of using built in rails routes and protections + * @description Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. + * @kind path-problem + * @problem.severity error + * @security-severity 5.0 + * @precision low + * @id rb/manually-checking-http-verb + * @tags security + */ + +import ruby +import codeql.ruby.DataFlow +import codeql.ruby.controlflow.CfgNodes +import codeql.ruby.frameworks.ActionController +import codeql.ruby.TaintTracking +import DataFlow::PathGraph + +// any `request` calls in an action method +class Request extends DataFlow::CallNode { + Request() { + this.getMethodName() = "request" and + this.asExpr().getExpr().getEnclosingMethod() instanceof ActionControllerActionMethod + } +} + +// `request.env` +class RequestEnvMethod extends DataFlow::CallNode { + RequestEnvMethod() { + this.getMethodName() = "env" and + any(Request r).flowsTo(this.getReceiver()) + } +} + +// `request.request_method` +class RequestRequestMethod extends DataFlow::CallNode { + RequestRequestMethod() { + this.getMethodName() = "request_method" and + any(Request r).flowsTo(this.getReceiver()) + } +} + +// `request.method` +class RequestMethod extends DataFlow::CallNode { + RequestMethod() { + this.getMethodName() = "method" and + any(Request r).flowsTo(this.getReceiver()) + } +} + +// `request.raw_request_method` +class RequestRawRequestMethod extends DataFlow::CallNode { + RequestRawRequestMethod() { + this.getMethodName() = "raw_request_method" and + any(Request r).flowsTo(this.getReceiver()) + } +} + +// `request.request_method_symbol` +class RequestRequestMethodSymbol extends DataFlow::CallNode { + RequestRequestMethodSymbol() { + this.getMethodName() = "request_method_symbol" and + any(Request r).flowsTo(this.getReceiver()) + } +} + +// `request.get?` +class RequestGet extends DataFlow::CallNode { + RequestGet() { + this.getMethodName() = "get?" and + any(Request r).flowsTo(this.getReceiver()) + } +} + +class HttpVerbConfig extends TaintTracking::Configuration { + HttpVerbConfig() { this = "HttpVerbConfig" } + + override predicate isSource(DataFlow::Node source) { + source instanceof RequestMethod or + source instanceof RequestRequestMethod or + source instanceof RequestEnvMethod or + source instanceof RequestRawRequestMethod or + source instanceof RequestRequestMethodSymbol or + source instanceof RequestGet + } + + override predicate isSink(DataFlow::Node sink) { + exists(ExprNodes::ConditionalExprCfgNode c | c.getCondition() = sink.asExpr()) or + exists(ExprNodes::CaseExprCfgNode c | c.getValue() = sink.asExpr()) + } +} + +from HttpVerbConfig config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, + "Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods." diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.qhelp b/ruby/ql/src/experimental/weak-params/WeakParams.qhelp new file mode 100644 index 00000000000..9bccf15d03d --- /dev/null +++ b/ruby/ql/src/experimental/weak-params/WeakParams.qhelp @@ -0,0 +1,28 @@ + + + +

    + Directly checking request parameters without following a strong params + pattern can lead to unintentional avenues for injection attacks. +

    +
    + +

    + Instead of manually checking parameters from the `param` object, it is + recommended that you follow the strong parameters pattern established in + Rails: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html +

    +

    + In the strong parameters pattern, you are able to specify required and allowed + parameters for each action called by your controller methods. This acts as an + additional layer of data validation before being passed along to other areas + of your application, such as the model. +

    +
    + + + + +
    diff --git a/ruby/ql/src/experimental/weak-params/WeakParams.ql b/ruby/ql/src/experimental/weak-params/WeakParams.ql new file mode 100644 index 00000000000..0c6d9db5644 --- /dev/null +++ b/ruby/ql/src/experimental/weak-params/WeakParams.ql @@ -0,0 +1,61 @@ +/** + * @name Weak or direct parameter references are used + * @description Directly checking request parameters without following a strong params pattern can lead to unintentional avenues for injection attacks. + * @kind path-problem + * @problem.severity error + * @security-severity 5.0 + * @precision medium + * @id rb/weak-params + * @tags security + */ + +import ruby +import codeql.ruby.Concepts +import codeql.ruby.DataFlow +import codeql.ruby.TaintTracking +import codeql.ruby.frameworks.ActionController +import DataFlow::PathGraph + +/** + * A call to `request` in an ActionController controller class. + * This probably refers to the incoming HTTP request object. + */ +class ActionControllerRequest extends DataFlow::Node { + ActionControllerRequest() { + exists(DataFlow::CallNode c | + c.asExpr().getExpr().getEnclosingModule() instanceof ActionControllerControllerClass and + c.getMethodName() = "request" + | + c.flowsTo(this) + ) + } +} + +/** + * A direct parameters reference that happens inside a controller class. + */ +class WeakParams extends DataFlow::CallNode { + WeakParams() { + this.getReceiver() instanceof ActionControllerRequest and + this.getMethodName() = + ["path_parameters", "query_parameters", "request_parameters", "GET", "POST"] + } +} + +/** + * A Taint tracking config where the source is a weak params access in a controller and the sink + * is a method call of a model class + */ +class Configuration extends TaintTracking::Configuration { + Configuration() { this = "WeakParamsConfiguration" } + + override predicate isSource(DataFlow::Node node) { node instanceof WeakParams } + + // the sink is an instance of a Model class that receives a method call + override predicate isSink(DataFlow::Node node) { node = any(PersistentWriteAccess a).getValue() } +} + +from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink +where config.hasFlowPath(source, sink) +select sink.getNode(), source, sink, + "By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html" diff --git a/ruby/ql/src/qlpack.yml b/ruby/ql/src/qlpack.yml index 0bd19b5bdeb..b713a6c49e3 100644 --- a/ruby/ql/src/qlpack.yml +++ b/ruby/ql/src/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/ruby-queries -version: 0.2.1-dev +version: 0.3.2-dev groups: - ruby - queries diff --git a/ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll b/ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll index 2192951f76d..5e9bc406512 100644 --- a/ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll +++ b/ruby/ql/src/queries/security/cwe-020/HostnameRegexpShared.qll @@ -53,7 +53,7 @@ predicate matchesBeginningOfString(RegExpTerm term) { } /** - * Holds if the given sequence contains top-level domain preceded by a dot, such as `.com`, + * Holds if the given sequence `seq` contains top-level domain preceded by a dot, such as `.com`, * excluding cases where this is at the very beginning of the regexp. * * `i` is bound to the index of the last child in the top-level domain part. diff --git a/ruby/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected index ebfd53fd551..4cb29e0f033 100644 --- a/ruby/ql/test/library-tests/ast/Ast.expected +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -1556,6 +1556,35 @@ constants/constants.rb: # 73| getAnOperand/getLeftOperand: [ClassVariableAccess] @@fourty_six # 73| getAnOperand/getRightOperand: [ConstantReadAccess] FOURTY_SIX # 73| getScopeExpr: [ConstantReadAccess] Mod3 +# 78| getStmt: [AssignExpr] ... = ... +# 78| getAnOperand/getLeftOperand: [LocalVariableAccess] a +# 78| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 78| getElement: [IntegerLiteral] 1 +# 78| getElement: [IntegerLiteral] 2 +# 78| getElement: [IntegerLiteral] 3 +# 79| getStmt: [AssignExpr] ... = ... +# 79| getAnOperand/getLeftOperand: [ConstantAssignment] A +# 79| getAnOperand/getRightOperand: [ArrayLiteral] [...] +# 79| getElement: [IntegerLiteral] 1 +# 79| getElement: [IntegerLiteral] 2 +# 79| getElement: [IntegerLiteral] 3 +# 80| getStmt: [AssignExpr] ... = ... +# 80| getAnOperand/getLeftOperand: [ConstantAssignment] B +# 80| getAnOperand/getRightOperand: [LocalVariableAccess] a +# 81| getStmt: [AssignExpr] ... = ... +# 81| getAnOperand/getLeftOperand: [ConstantAssignment] C +# 81| getAnOperand/getRightOperand: [ConstantReadAccess] A +# 82| getStmt: [AssignExpr] ... = ... +# 82| getAnOperand/getLeftOperand: [LocalVariableAccess] b +# 82| getAnOperand/getRightOperand: [ConstantReadAccess] B +# 84| getStmt: [IfExpr] if ... +# 84| getCondition: [MethodCall] call to condition +# 84| getReceiver: [SelfVariableAccess] self +# 84| getBranch/getThen: [StmtSequence] then ... +# 85| getStmt: [AssignExpr] ... = ... +# 85| getAnOperand/getLeftOperand: [LocalVariableAccess] c +# 85| getAnOperand/getRightOperand: [LocalVariableAccess] b +# 87| getStmt: [LocalVariableAccess] c escape_sequences/escapes.rb: # 1| [Toplevel] escapes.rb # 6| getStmt: [StringLiteral] "\'" @@ -1733,6 +1762,12 @@ escape_sequences/escapes.rb: # 93| getStmt: [SymbolLiteral] :"\C-?" # 93| getComponent: [StringEscapeSequenceComponent] \C # 93| getComponent: [StringTextComponent] -? +misc/iso-8859-15.rb: +# 1| [Toplevel] iso-8859-15.rb +# 4| getStmt: [MethodCall] call to print +# 4| getReceiver: [SelfVariableAccess] self +# 4| getArgument: [StringLiteral] "EUR = €" +# 4| getComponent: [StringTextComponent] EUR = € literals/literals.rb: # 1| [Toplevel] literals.rb # 2| getStmt: [NilLiteral] nil diff --git a/ruby/ql/test/library-tests/ast/AstDesugar.expected b/ruby/ql/test/library-tests/ast/AstDesugar.expected index 0e6a0694105..8be5246ab88 100644 --- a/ruby/ql/test/library-tests/ast/AstDesugar.expected +++ b/ruby/ql/test/library-tests/ast/AstDesugar.expected @@ -86,10 +86,10 @@ calls/calls.rb: # 316| getStmt: [SetterMethodCall] call to foo= # 316| getReceiver: [SelfVariableAccess] self # 316| getArgument: [AssignExpr] ... = ... -# 316| getAnOperand/getRightOperand: [MethodCall] call to [] -# 316| getArgument: [IntegerLiteral] 0 -# 316| getReceiver: [LocalVariableAccess] __synth__0 # 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 +# 316| getAnOperand/getRightOperand: [MethodCall] call to [] +# 316| getReceiver: [LocalVariableAccess] __synth__0 +# 316| getArgument: [IntegerLiteral] 0 # 316| getStmt: [LocalVariableAccess] __synth__0__1 # 316| getStmt: [AssignExpr] ... = ... # 316| getAnOperand/getLeftOperand: [MethodCall] call to bar @@ -97,12 +97,12 @@ calls/calls.rb: # 316| getStmt: [SetterMethodCall] call to bar= # 316| getReceiver: [SelfVariableAccess] self # 316| getArgument: [AssignExpr] ... = ... +# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 316| getAnOperand/getRightOperand: [MethodCall] call to [] +# 316| getReceiver: [LocalVariableAccess] __synth__0 # 316| getArgument: [RangeLiteral] _ .. _ # 316| getBegin: [IntegerLiteral] 1 # 316| getEnd: [IntegerLiteral] -2 -# 316| getReceiver: [LocalVariableAccess] __synth__0 -# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 316| getStmt: [LocalVariableAccess] __synth__0__1 # 316| getStmt: [AssignExpr] ... = ... # 316| getAnOperand/getLeftOperand: [ElementReference] ...[...] @@ -111,13 +111,14 @@ calls/calls.rb: # 316| getReceiver: [MethodCall] call to foo # 316| getReceiver: [SelfVariableAccess] self # 316| getArgument: [AssignExpr] ... = ... -# 316| getAnOperand/getRightOperand: [MethodCall] call to [] -# 316| getArgument: [IntegerLiteral] -1 -# 316| getReceiver: [LocalVariableAccess] __synth__0 # 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 +# 316| getAnOperand/getRightOperand: [MethodCall] call to [] +# 316| getReceiver: [LocalVariableAccess] __synth__0 +# 316| getArgument: [IntegerLiteral] -1 # 316| getArgument: [IntegerLiteral] 4 # 316| getStmt: [LocalVariableAccess] __synth__0__1 # 316| getStmt: [AssignExpr] ... = ... +# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 # 316| getAnOperand/getRightOperand: [SplatExpr] * ... # 316| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...] # 316| getDesugared: [MethodCall] call to [] @@ -126,14 +127,13 @@ calls/calls.rb: # 316| getArgument: [IntegerLiteral] 2 # 316| getArgument: [IntegerLiteral] 3 # 316| getArgument: [IntegerLiteral] 4 -# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 # 317| [AssignExpr] ... = ... # 317| getDesugared: [StmtSequence] ... # 317| getStmt: [AssignExpr] ... = ... # 317| getAnOperand/getLeftOperand: [LocalVariableAccess] a # 317| getAnOperand/getRightOperand: [MethodCall] call to [] -# 317| getArgument: [IntegerLiteral] 0 # 317| getReceiver: [LocalVariableAccess] __synth__0 +# 317| getArgument: [IntegerLiteral] 0 # 317| getStmt: [AssignExpr] ... = ... # 317| getAnOperand/getLeftOperand: [ElementReference] ...[...] # 317| getDesugared: [StmtSequence] ... @@ -141,15 +141,16 @@ calls/calls.rb: # 317| getReceiver: [MethodCall] call to foo # 317| getReceiver: [SelfVariableAccess] self # 317| getArgument: [AssignExpr] ... = ... +# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 317| getAnOperand/getRightOperand: [MethodCall] call to [] +# 317| getReceiver: [LocalVariableAccess] __synth__0 # 317| getArgument: [RangeLiteral] _ .. _ # 317| getBegin: [IntegerLiteral] 1 # 317| getEnd: [IntegerLiteral] -1 -# 317| getReceiver: [LocalVariableAccess] __synth__0 -# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 317| getArgument: [IntegerLiteral] 5 # 317| getStmt: [LocalVariableAccess] __synth__0__1 # 317| getStmt: [AssignExpr] ... = ... +# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 # 317| getAnOperand/getRightOperand: [SplatExpr] * ... # 317| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...] # 317| getDesugared: [MethodCall] call to [] @@ -157,7 +158,6 @@ calls/calls.rb: # 317| getArgument: [IntegerLiteral] 1 # 317| getArgument: [IntegerLiteral] 2 # 317| getArgument: [IntegerLiteral] 3 -# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0 # 318| [AssignAddExpr] ... += ... # 318| getDesugared: [StmtSequence] ... # 318| getStmt: [AssignExpr] ... = ... @@ -167,11 +167,11 @@ calls/calls.rb: # 318| getReceiver: [LocalVariableAccess] __synth__0 # 318| getArgument: [LocalVariableAccess] __synth__1 # 318| getStmt: [AssignExpr] ... = ... +# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1 # 318| getAnOperand/getRightOperand: [AddExpr] ... + ... # 318| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to count # 318| getReceiver: [LocalVariableAccess] __synth__0 # 318| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 -# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1 # 318| getStmt: [LocalVariableAccess] __synth__1 # 319| [AssignAddExpr] ... += ... # 319| getDesugared: [StmtSequence] ... @@ -187,12 +187,12 @@ calls/calls.rb: # 319| getAnOperand/getRightOperand: [IntegerLiteral] 0 # 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1 # 319| getStmt: [AssignExpr] ... = ... +# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2 # 319| getAnOperand/getRightOperand: [AddExpr] ... + ... # 319| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to [] # 319| getReceiver: [LocalVariableAccess] __synth__0 # 319| getArgument: [LocalVariableAccess] __synth__1 # 319| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 -# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2 # 319| getStmt: [LocalVariableAccess] __synth__2 # 320| [AssignMulExpr] ... *= ... # 320| getDesugared: [StmtSequence] ... @@ -223,6 +223,7 @@ calls/calls.rb: # 320| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1 # 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3 # 320| getStmt: [AssignExpr] ... = ... +# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__4 # 320| getAnOperand/getRightOperand: [MulExpr] ... * ... # 320| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to [] # 320| getReceiver: [LocalVariableAccess] __synth__0 @@ -230,7 +231,6 @@ calls/calls.rb: # 320| getArgument: [LocalVariableAccess] __synth__2 # 320| getArgument: [LocalVariableAccess] __synth__3 # 320| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2 -# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__4 # 320| getStmt: [LocalVariableAccess] __synth__4 # 340| [ForExpr] for ... in ... # 340| getDesugared: [MethodCall] call to each @@ -240,24 +240,24 @@ calls/calls.rb: # 340| getStmt: [AssignExpr] ... = ... # 340| getDesugared: [StmtSequence] ... # 340| getStmt: [AssignExpr] ... = ... +# 340| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 340| getAnOperand/getRightOperand: [SplatExpr] * ... # 340| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1 -# 340| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 340| getStmt: [AssignExpr] ... = ... # 340| getAnOperand/getLeftOperand: [LocalVariableAccess] x # 340| getAnOperand/getRightOperand: [MethodCall] call to [] -# 340| getArgument: [IntegerLiteral] 0 # 340| getReceiver: [LocalVariableAccess] __synth__0__1 +# 340| getArgument: [IntegerLiteral] 0 # 340| getStmt: [AssignExpr] ... = ... # 340| getAnOperand/getLeftOperand: [LocalVariableAccess] y # 340| getAnOperand/getRightOperand: [MethodCall] call to [] -# 340| getArgument: [IntegerLiteral] 1 # 340| getReceiver: [LocalVariableAccess] __synth__0__1 +# 340| getArgument: [IntegerLiteral] 1 # 340| getStmt: [AssignExpr] ... = ... # 340| getAnOperand/getLeftOperand: [LocalVariableAccess] z # 340| getAnOperand/getRightOperand: [MethodCall] call to [] -# 340| getArgument: [IntegerLiteral] 2 # 340| getReceiver: [LocalVariableAccess] __synth__0__1 +# 340| getArgument: [IntegerLiteral] 2 # 340| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...) # 341| getStmt: [MethodCall] call to foo # 341| getReceiver: [SelfVariableAccess] self @@ -286,9 +286,9 @@ calls/calls.rb: # 362| getReceiver: [SelfVariableAccess] self # 362| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 362| getStmt: [IfExpr] if ... +# 362| getBranch/getThen: [NilLiteral] nil # 362| getBranch/getElse: [MethodCall] call to empty? # 362| getReceiver: [LocalVariableAccess] __synth__0__1 -# 362| getBranch/getThen: [NilLiteral] nil # 362| getCondition: [MethodCall] call to == # 362| getArgument: [LocalVariableAccess] __synth__0__1 # 362| getReceiver: [NilLiteral] nil @@ -299,6 +299,7 @@ calls/calls.rb: # 364| getReceiver: [SelfVariableAccess] self # 364| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 364| getStmt: [IfExpr] if ... +# 364| getBranch/getThen: [NilLiteral] nil # 364| getBranch/getElse: [MethodCall] call to bar # 364| getReceiver: [LocalVariableAccess] __synth__0__1 # 364| getArgument: [IntegerLiteral] 1 @@ -307,7 +308,6 @@ calls/calls.rb: # 364| getParameter: [SimpleParameter] x # 364| getDefiningAccess: [LocalVariableAccess] x # 364| getStmt: [LocalVariableAccess] x -# 364| getBranch/getThen: [NilLiteral] nil # 364| getCondition: [MethodCall] call to == # 364| getArgument: [LocalVariableAccess] __synth__0__1 # 364| getReceiver: [NilLiteral] nil @@ -336,6 +336,18 @@ constants/constants.rb: # 20| getComponent: [StringTextComponent] Chuck # 20| getArgument: [StringLiteral] "Dave" # 20| getComponent: [StringTextComponent] Dave +# 78| [ArrayLiteral] [...] +# 78| getDesugared: [MethodCall] call to [] +# 78| getReceiver: [ConstantReadAccess] Array +# 78| getArgument: [IntegerLiteral] 1 +# 78| getArgument: [IntegerLiteral] 2 +# 78| getArgument: [IntegerLiteral] 3 +# 79| [ArrayLiteral] [...] +# 79| getDesugared: [MethodCall] call to [] +# 79| getReceiver: [ConstantReadAccess] Array +# 79| getArgument: [IntegerLiteral] 1 +# 79| getArgument: [IntegerLiteral] 2 +# 79| getArgument: [IntegerLiteral] 3 escape_sequences/escapes.rb: # 58| [ArrayLiteral] %w(...) # 58| getDesugared: [MethodCall] call to [] @@ -596,19 +608,19 @@ control/loops.rb: # 22| getStmt: [AssignExpr] ... = ... # 22| getDesugared: [StmtSequence] ... # 22| getStmt: [AssignExpr] ... = ... +# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 22| getAnOperand/getRightOperand: [SplatExpr] * ... # 22| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1 -# 22| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 22| getStmt: [AssignExpr] ... = ... # 22| getAnOperand/getLeftOperand: [LocalVariableAccess] key # 22| getAnOperand/getRightOperand: [MethodCall] call to [] -# 22| getArgument: [IntegerLiteral] 0 # 22| getReceiver: [LocalVariableAccess] __synth__0__1 +# 22| getArgument: [IntegerLiteral] 0 # 22| getStmt: [AssignExpr] ... = ... # 22| getAnOperand/getLeftOperand: [LocalVariableAccess] value # 22| getAnOperand/getRightOperand: [MethodCall] call to [] -# 22| getArgument: [IntegerLiteral] 1 # 22| getReceiver: [LocalVariableAccess] __synth__0__1 +# 22| getArgument: [IntegerLiteral] 1 # 22| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...) # 23| getStmt: [AssignAddExpr] ... += ... # 23| getDesugared: [AssignExpr] ... = ... @@ -641,19 +653,19 @@ control/loops.rb: # 28| getStmt: [AssignExpr] ... = ... # 28| getDesugared: [StmtSequence] ... # 28| getStmt: [AssignExpr] ... = ... +# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 28| getAnOperand/getRightOperand: [SplatExpr] * ... # 28| getAnOperand/getOperand/getReceiver: [LocalVariableAccess] __synth__0__1 -# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1 # 28| getStmt: [AssignExpr] ... = ... # 28| getAnOperand/getLeftOperand: [LocalVariableAccess] key # 28| getAnOperand/getRightOperand: [MethodCall] call to [] -# 28| getArgument: [IntegerLiteral] 0 # 28| getReceiver: [LocalVariableAccess] __synth__0__1 +# 28| getArgument: [IntegerLiteral] 0 # 28| getStmt: [AssignExpr] ... = ... # 28| getAnOperand/getLeftOperand: [LocalVariableAccess] value # 28| getAnOperand/getRightOperand: [MethodCall] call to [] -# 28| getArgument: [IntegerLiteral] 1 # 28| getReceiver: [LocalVariableAccess] __synth__0__1 +# 28| getArgument: [IntegerLiteral] 1 # 28| getAnOperand/getLeftOperand: [DestructuredLhsExpr] (..., ...) # 29| getStmt: [AssignAddExpr] ... += ... # 29| getDesugared: [AssignExpr] ... = ... diff --git a/ruby/ql/test/library-tests/ast/TreeSitter.expected b/ruby/ql/test/library-tests/ast/TreeSitter.expected index 0a2ecda8d28..cb5a17ebebb 100644 --- a/ruby/ql/test/library-tests/ast/TreeSitter.expected +++ b/ruby/ql/test/library-tests/ast/TreeSitter.expected @@ -1656,10 +1656,56 @@ constants/constants.rb: # 73| 1: [ReservedWord] :: # 73| 2: [Constant] FOURTY_SIX # 74| 5: [ReservedWord] end +# 78| 13: [Assignment] Assignment +# 78| 0: [Identifier] a +# 78| 1: [ReservedWord] = +# 78| 2: [Array] Array +# 78| 0: [ReservedWord] [ +# 78| 1: [Integer] 1 +# 78| 2: [ReservedWord] , +# 78| 3: [Integer] 2 +# 78| 4: [ReservedWord] , +# 78| 5: [Integer] 3 +# 78| 6: [ReservedWord] ] +# 79| 14: [Assignment] Assignment +# 79| 0: [Constant] A +# 79| 1: [ReservedWord] = +# 79| 2: [Array] Array +# 79| 0: [ReservedWord] [ +# 79| 1: [Integer] 1 +# 79| 2: [ReservedWord] , +# 79| 3: [Integer] 2 +# 79| 4: [ReservedWord] , +# 79| 5: [Integer] 3 +# 79| 6: [ReservedWord] ] +# 80| 15: [Assignment] Assignment +# 80| 0: [Constant] B +# 80| 1: [ReservedWord] = +# 80| 2: [Identifier] a +# 81| 16: [Assignment] Assignment +# 81| 0: [Constant] C +# 81| 1: [ReservedWord] = +# 81| 2: [Constant] A +# 82| 17: [Assignment] Assignment +# 82| 0: [Identifier] b +# 82| 1: [ReservedWord] = +# 82| 2: [Constant] B +# 84| 18: [If] If +# 84| 0: [ReservedWord] if +# 84| 1: [Identifier] condition +# 84| 2: [Then] Then +# 85| 0: [Assignment] Assignment +# 85| 0: [Identifier] c +# 85| 1: [ReservedWord] = +# 85| 2: [Identifier] b +# 86| 3: [ReservedWord] end +# 87| 19: [Identifier] c # 26| [Comment] # A call to Kernel::Array; despite beginning with an upper-case character, # 27| [Comment] # we don't consider this to be a constant access. # 55| [Comment] # refers to ::ModuleA::FOURTY_FOUR # 57| [Comment] # refers to ::ModuleA::ModuleB::ClassB::FOURTY_FOUR +# 76| [Comment] # Array constants +# 87| [Comment] # not recognised control/cases.rb: # 1| [Program] Program # 2| 0: [Assignment] Assignment @@ -4558,6 +4604,17 @@ literals/literals.rb: # 193| cat file.txt # 193| # 195| 1: [HeredocEnd] SCRIPT +misc/iso-8859-15.rb: +# 1| [Program] Program +# 4| 0: [Call] Call +# 4| 0: [Identifier] print +# 4| 1: [ArgumentList] ArgumentList +# 4| 0: [String] String +# 4| 0: [ReservedWord] " +# 4| 1: [StringContent] EUR = € +# 4| 2: [ReservedWord] " +# 1| [Comment] #! /usr/bin/ruby +# 2| [Comment] # coding: iso-8859-15 misc/misc.erb: # 2| [Program] Program # 2| 0: [Call] Call diff --git a/ruby/ql/test/library-tests/ast/ValueText.expected b/ruby/ql/test/library-tests/ast/ValueText.expected index 6225b1b2e90..e66838fbe2c 100644 --- a/ruby/ql/test/library-tests/ast/ValueText.expected +++ b/ruby/ql/test/library-tests/ast/ValueText.expected @@ -109,6 +109,12 @@ exprValue | constants/constants.rb:63:19:63:20 | 45 | 45 | int | | constants/constants.rb:65:19:65:35 | FOURTY_FIVE | 45 | int | | constants/constants.rb:71:18:71:19 | 46 | 46 | int | +| constants/constants.rb:78:6:78:6 | 1 | 1 | int | +| constants/constants.rb:78:9:78:9 | 2 | 2 | int | +| constants/constants.rb:78:12:78:12 | 3 | 3 | int | +| constants/constants.rb:79:6:79:6 | 1 | 1 | int | +| constants/constants.rb:79:9:79:9 | 2 | 2 | int | +| constants/constants.rb:79:12:79:12 | 3 | 3 | int | | control/cases.rb:2:5:2:5 | 0 | 0 | int | | control/cases.rb:3:5:3:5 | 0 | 0 | int | | control/cases.rb:4:5:4:5 | 0 | 0 | int | @@ -711,6 +717,7 @@ exprValue | literals/literals.rb:198:8:198:8 | 5 | 5 | int | | literals/literals.rb:199:2:199:2 | :y | :y | symbol | | literals/literals.rb:199:7:199:7 | :Z | :Z | symbol | +| misc/iso-8859-15.rb:4:7:4:17 | "EUR = \u20ac" | EUR = \u20ac | string | | misc/misc.erb:2:15:2:37 | "main_include_admin.js" | main_include_admin.js | string | | misc/misc.rb:1:7:1:11 | "bar" | bar | string | | misc/misc.rb:3:7:3:9 | foo | foo | string | @@ -1004,6 +1011,12 @@ exprCfgNodeValue | constants/constants.rb:63:19:63:20 | 45 | 45 | int | | constants/constants.rb:65:19:65:35 | FOURTY_FIVE | 45 | int | | constants/constants.rb:71:18:71:19 | 46 | 46 | int | +| constants/constants.rb:78:6:78:6 | 1 | 1 | int | +| constants/constants.rb:78:9:78:9 | 2 | 2 | int | +| constants/constants.rb:78:12:78:12 | 3 | 3 | int | +| constants/constants.rb:79:6:79:6 | 1 | 1 | int | +| constants/constants.rb:79:9:79:9 | 2 | 2 | int | +| constants/constants.rb:79:12:79:12 | 3 | 3 | int | | control/cases.rb:2:5:2:5 | 0 | 0 | int | | control/cases.rb:3:5:3:5 | 0 | 0 | int | | control/cases.rb:4:5:4:5 | 0 | 0 | int | @@ -1580,6 +1593,7 @@ exprCfgNodeValue | literals/literals.rb:198:8:198:8 | 5 | 5 | int | | literals/literals.rb:199:2:199:2 | :y | :y | symbol | | literals/literals.rb:199:7:199:7 | :Z | :Z | symbol | +| misc/iso-8859-15.rb:4:7:4:17 | "EUR = \u20ac" | EUR = \u20ac | string | | misc/misc.erb:2:15:2:37 | "main_include_admin.js" | main_include_admin.js | string | | misc/misc.rb:1:7:1:11 | "bar" | bar | string | | misc/misc.rb:3:7:3:9 | foo | foo | string | diff --git a/ruby/ql/test/library-tests/ast/constants/constants.expected b/ruby/ql/test/library-tests/ast/constants/constants.expected index 89b5512921c..3f189cc26fd 100644 --- a/ruby/ql/test/library-tests/ast/constants/constants.expected +++ b/ruby/ql/test/library-tests/ast/constants/constants.expected @@ -61,6 +61,13 @@ constantAccess | constants.rb:71:5:71:14 | FOURTY_SIX | write | FOURTY_SIX | ConstantAssignment | | constants.rb:73:18:73:21 | Mod3 | read | Mod3 | ConstantReadAccess | | constants.rb:73:18:73:33 | FOURTY_SIX | read | FOURTY_SIX | ConstantReadAccess | +| constants.rb:78:5:78:13 | Array | read | Array | ConstantReadAccess | +| constants.rb:79:1:79:1 | A | write | A | ConstantAssignment | +| constants.rb:79:5:79:13 | Array | read | Array | ConstantReadAccess | +| constants.rb:80:1:80:1 | B | write | B | ConstantAssignment | +| constants.rb:81:1:81:1 | C | write | C | ConstantAssignment | +| constants.rb:81:5:81:5 | A | read | A | ConstantReadAccess | +| constants.rb:82:5:82:5 | B | read | B | ConstantReadAccess | getConst | constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" | | constants.rb:1:1:15:3 | ModuleA | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" | @@ -71,23 +78,41 @@ getConst | constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_ONE | constants.rb:48:18:48:19 | 41 | | constants.rb:62:3:64:5 | Mod1::Mod3 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 | | constants.rb:70:3:72:5 | Mod1::Mod3::Mod5 | FOURTY_SIX | constants.rb:71:18:71:19 | 46 | +| file://:0:0:0:0 | Object | A | constants.rb:79:5:79:13 | [...] | +| file://:0:0:0:0 | Object | B | constants.rb:80:5:80:5 | a | +| file://:0:0:0:0 | Object | C | constants.rb:81:5:81:5 | A | | file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... | lookupConst | constants.rb:1:1:15:3 | ModuleA | CONST_B | constants.rb:6:15:6:23 | "const_b" | | constants.rb:1:1:15:3 | ModuleA | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" | +| constants.rb:2:5:4:7 | ModuleA::ClassA | A | constants.rb:79:5:79:13 | [...] | +| constants.rb:2:5:4:7 | ModuleA::ClassA | B | constants.rb:80:5:80:5 | a | +| constants.rb:2:5:4:7 | ModuleA::ClassA | C | constants.rb:81:5:81:5 | A | | constants.rb:2:5:4:7 | ModuleA::ClassA | CONST_A | constants.rb:3:19:3:27 | "const_a" | | constants.rb:2:5:4:7 | ModuleA::ClassA | GREETING | constants.rb:17:12:17:64 | ... + ... | | constants.rb:8:5:14:7 | ModuleA::ModuleB | MAX_SIZE | constants.rb:39:30:39:33 | 1024 | +| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | A | constants.rb:79:5:79:13 | [...] | +| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | B | constants.rb:80:5:80:5 | a | +| constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | C | constants.rb:81:5:81:5 | A | | constants.rb:12:9:13:11 | ModuleA::ModuleB::ClassC | GREETING | constants.rb:17:12:17:64 | ... + ... | +| constants.rb:31:1:33:3 | ModuleA::ClassD | A | constants.rb:79:5:79:13 | [...] | +| constants.rb:31:1:33:3 | ModuleA::ClassD | B | constants.rb:80:5:80:5 | a | +| constants.rb:31:1:33:3 | ModuleA::ClassD | C | constants.rb:81:5:81:5 | A | | constants.rb:31:1:33:3 | ModuleA::ClassD | CONST_A | constants.rb:3:19:3:27 | "const_a" | | constants.rb:31:1:33:3 | ModuleA::ClassD | FOURTY_TWO | constants.rb:32:16:32:17 | 42 | | constants.rb:31:1:33:3 | ModuleA::ClassD | GREETING | constants.rb:17:12:17:64 | ... + ... | | constants.rb:35:1:37:3 | ModuleA::ModuleC | FOURTY_THREE | constants.rb:36:18:36:19 | 43 | +| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | A | constants.rb:79:5:79:13 | [...] | +| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | B | constants.rb:80:5:80:5 | a | +| constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | C | constants.rb:81:5:81:5 | A | | constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_FOUR | constants.rb:56:19:56:20 | 44 | | constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | FOURTY_ONE | constants.rb:48:18:48:19 | 41 | | constants.rb:54:3:58:5 | ModuleA::ModuleB::ClassB | GREETING | constants.rb:17:12:17:64 | ... + ... | | constants.rb:62:3:64:5 | Mod1::Mod3 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 | | constants.rb:70:3:72:5 | Mod1::Mod3::Mod5 | FOURTY_SIX | constants.rb:71:18:71:19 | 46 | +| file://:0:0:0:0 | Object | A | constants.rb:79:5:79:13 | [...] | +| file://:0:0:0:0 | Object | B | constants.rb:80:5:80:5 | a | +| file://:0:0:0:0 | Object | C | constants.rb:81:5:81:5 | A | | file://:0:0:0:0 | Object | GREETING | constants.rb:17:12:17:64 | ... + ... | constantValue | constants.rb:17:22:17:45 | CONST_A | constants.rb:3:19:3:27 | "const_a" | @@ -101,6 +126,8 @@ constantValue | constants.rb:57:21:57:31 | FOURTY_FOUR | constants.rb:53:17:53:29 | "fourty-four" | | constants.rb:57:21:57:31 | FOURTY_FOUR | constants.rb:56:19:56:20 | 44 | | constants.rb:65:19:65:35 | FOURTY_FIVE | constants.rb:63:19:63:20 | 45 | +| constants.rb:81:5:81:5 | A | constants.rb:79:5:79:13 | [...] | +| constants.rb:82:5:82:5 | B | constants.rb:80:5:80:5 | a | constantWriteAccessQualifiedName | constants.rb:1:1:15:3 | ModuleA | ModuleA | | constants.rb:2:5:4:7 | ClassA | ModuleA::ClassA | @@ -133,3 +160,14 @@ constantWriteAccessQualifiedName | constants.rb:70:3:72:5 | Mod5 | Mod3::Mod5 | | constants.rb:71:5:71:14 | FOURTY_SIX | Mod1::Mod3::Mod5::FOURTY_SIX | | constants.rb:71:5:71:14 | FOURTY_SIX | Mod3::Mod5::FOURTY_SIX | +| constants.rb:79:1:79:1 | A | A | +| constants.rb:80:1:80:1 | B | B | +| constants.rb:81:1:81:1 | C | C | +arrayConstant +| constants.rb:20:13:20:37 | call to [] | constants.rb:20:13:20:37 | call to [] | +| constants.rb:78:5:78:13 | call to [] | constants.rb:78:5:78:13 | call to [] | +| constants.rb:79:5:79:13 | call to [] | constants.rb:79:5:79:13 | call to [] | +| constants.rb:80:5:80:5 | a | constants.rb:78:5:78:13 | call to [] | +| constants.rb:81:5:81:5 | A | constants.rb:79:5:79:13 | call to [] | +| constants.rb:82:5:82:5 | B | constants.rb:78:5:78:13 | call to [] | +| constants.rb:85:7:85:7 | b | constants.rb:78:5:78:13 | call to [] | diff --git a/ruby/ql/test/library-tests/ast/constants/constants.ql b/ruby/ql/test/library-tests/ast/constants/constants.ql index 66c0d230d99..37140a957a3 100644 --- a/ruby/ql/test/library-tests/ast/constants/constants.ql +++ b/ruby/ql/test/library-tests/ast/constants/constants.ql @@ -1,5 +1,6 @@ import ruby import codeql.ruby.ast.internal.Module as M +import codeql.ruby.ast.internal.Constant query predicate constantAccess(ConstantAccess a, string kind, string name, string cls) { ( @@ -20,3 +21,5 @@ query predicate constantValue(ConstantReadAccess a, Expr e) { e = a.getValue() } query predicate constantWriteAccessQualifiedName(ConstantWriteAccess w, string qualifiedName) { w.getAQualifiedName() = qualifiedName } + +query predicate arrayConstant = isArrayConstant/2; diff --git a/ruby/ql/test/library-tests/ast/constants/constants.rb b/ruby/ql/test/library-tests/ast/constants/constants.rb index 08e0d4216a9..359a861eb1e 100644 --- a/ruby/ql/test/library-tests/ast/constants/constants.rb +++ b/ruby/ql/test/library-tests/ast/constants/constants.rb @@ -72,3 +72,16 @@ module Mod4 end @@fourty_six = Mod3::FOURTY_SIX end + +# Array constants + +a = [1, 2, 3] +A = [1, 2, 3] +B = a +C = A +b = B + +if condition + c = b +end +c # not recognised diff --git a/ruby/ql/test/library-tests/ast/misc/iso-8859-15.rb b/ruby/ql/test/library-tests/ast/misc/iso-8859-15.rb new file mode 100644 index 00000000000..d5fd8ae9456 --- /dev/null +++ b/ruby/ql/test/library-tests/ast/misc/iso-8859-15.rb @@ -0,0 +1,4 @@ +#! /usr/bin/ruby +# coding: iso-8859-15 + +print "EUR = ¤" \ No newline at end of file diff --git a/ruby/ql/test/library-tests/concepts/PersistentWriteAccess.expected b/ruby/ql/test/library-tests/concepts/PersistentWriteAccess.expected index f71e5f89116..64d01e3e93d 100644 --- a/ruby/ql/test/library-tests/concepts/PersistentWriteAccess.expected +++ b/ruby/ql/test/library-tests/concepts/PersistentWriteAccess.expected @@ -15,6 +15,11 @@ | app/controllers/users_controller.rb:23:7:23:42 | call to update_attribute | app/controllers/users_controller.rb:23:37:23:41 | "U13" | | app/controllers/users_controller.rb:26:19:26:23 | ... = ... | app/controllers/users_controller.rb:26:19:26:23 | "U14" | | app/controllers/users_controller.rb:31:7:31:32 | call to touch_all | app/controllers/users_controller.rb:31:28:31:31 | call to time | +| app/controllers/users_controller.rb:35:7:35:27 | call to update | app/controllers/users_controller.rb:35:22:35:26 | attrs | +| app/controllers/users_controller.rb:36:7:36:28 | call to update! | app/controllers/users_controller.rb:36:23:36:27 | attrs | +| app/controllers/users_controller.rb:39:7:39:24 | call to create | app/controllers/users_controller.rb:39:19:39:23 | attrs | +| app/controllers/users_controller.rb:40:7:40:25 | call to create! | app/controllers/users_controller.rb:40:20:40:24 | attrs | +| app/controllers/users_controller.rb:41:7:41:24 | call to insert | app/controllers/users_controller.rb:41:19:41:23 | attrs | | app/models/user.rb:4:5:4:28 | call to update | app/models/user.rb:4:23:4:27 | "U15" | | app/models/user.rb:5:5:5:23 | call to update | app/models/user.rb:5:18:5:22 | "U16" | | app/models/user.rb:6:5:6:56 | call to update_attributes | app/models/user.rb:6:35:6:39 | "U17" | diff --git a/ruby/ql/test/library-tests/concepts/app/controllers/users_controller.rb b/ruby/ql/test/library-tests/concepts/app/controllers/users_controller.rb index 83ee6088527..062c643e0ae 100644 --- a/ruby/ql/test/library-tests/concepts/app/controllers/users_controller.rb +++ b/ruby/ql/test/library-tests/concepts/app/controllers/users_controller.rb @@ -29,6 +29,16 @@ module Users # TouchAllCall User.touch_all User.touch_all(time: time) + + # UpdateLikeClassMethodCall + attrs = {name: "U15"} + User.update(8, attrs) + User.update!(8, attrs) + + # CreateLikeClassMethodCall + User.create(attrs) + User.create!(attrs) + User.insert(attrs) end def get_uid diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected new file mode 100644 index 00000000000..783d414dad6 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.expected @@ -0,0 +1,22 @@ +WARNING: Type BarrierGuard has been deprecated and may be removed in future (barrier-guards.ql:8,3-15) +oldStyleBarrierGuards +| barrier-guards.rb:3:4:3:15 | ... == ... | barrier-guards.rb:4:5:4:7 | foo | barrier-guards.rb:3:4:3:6 | foo | true | +| barrier-guards.rb:9:4:9:24 | call to include? | barrier-guards.rb:10:5:10:7 | foo | barrier-guards.rb:9:21:9:23 | foo | true | +| barrier-guards.rb:15:4:15:15 | ... != ... | barrier-guards.rb:18:5:18:7 | foo | barrier-guards.rb:15:4:15:6 | foo | false | +| barrier-guards.rb:21:8:21:19 | ... == ... | barrier-guards.rb:24:5:24:7 | foo | barrier-guards.rb:21:8:21:10 | foo | true | +| barrier-guards.rb:27:8:27:19 | ... != ... | barrier-guards.rb:28:5:28:7 | foo | barrier-guards.rb:27:8:27:10 | foo | false | +| barrier-guards.rb:37:4:37:20 | call to include? | barrier-guards.rb:38:5:38:7 | foo | barrier-guards.rb:37:17:37:19 | foo | true | +| barrier-guards.rb:43:4:43:15 | ... == ... | barrier-guards.rb:45:9:45:11 | foo | barrier-guards.rb:43:4:43:6 | foo | true | +| barrier-guards.rb:70:4:70:21 | call to include? | barrier-guards.rb:71:5:71:7 | foo | barrier-guards.rb:70:18:70:20 | foo | true | +| barrier-guards.rb:82:4:82:25 | ... != ... | barrier-guards.rb:83:5:83:7 | foo | barrier-guards.rb:82:15:82:17 | foo | true | +newStyleBarrierGuards +| barrier-guards.rb:4:5:4:7 | foo | +| barrier-guards.rb:10:5:10:7 | foo | +| barrier-guards.rb:18:5:18:7 | foo | +| barrier-guards.rb:24:5:24:7 | foo | +| barrier-guards.rb:28:5:28:7 | foo | +| barrier-guards.rb:38:5:38:7 | foo | +| barrier-guards.rb:45:9:45:11 | foo | +| barrier-guards.rb:71:5:71:7 | foo | +| barrier-guards.rb:83:5:83:7 | foo | +| barrier-guards.rb:91:5:91:7 | foo | diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql new file mode 100644 index 00000000000..84a962ade35 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.ql @@ -0,0 +1,16 @@ +import codeql.ruby.dataflow.internal.DataFlowPublic +import codeql.ruby.dataflow.BarrierGuards +import codeql.ruby.controlflow.CfgNodes +import codeql.ruby.controlflow.ControlFlowGraph +import codeql.ruby.DataFlow + +query predicate oldStyleBarrierGuards( + BarrierGuard g, DataFlow::Node guardedNode, ExprCfgNode expr, boolean branch +) { + g.checks(expr, branch) and guardedNode = g.getAGuardedNode() +} + +query predicate newStyleBarrierGuards(DataFlow::Node n) { + n instanceof StringConstCompareBarrier or + n instanceof StringConstArrayInclusionCallBarrier +} diff --git a/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb new file mode 100644 index 00000000000..47b96da22dd --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/barrier-guards/barrier-guards.rb @@ -0,0 +1,104 @@ +foo = "foo" + +if foo == "foo" + foo +else + foo +end + +if ["foo"].include?(foo) + foo +else + foo +end + +if foo != "foo" + foo +else + foo +end + +unless foo == "foo" + foo +else + foo +end + +unless foo != "foo" + foo +else + foo +end + +foo + +FOO = ["foo"] + +if FOO.include?(foo) + foo +else + foo +end + +if foo == "foo" + capture { + foo # guarded + } +end + +if foo == "foo" + capture { + foo = "bar" + foo # not guarded + } +end + +if foo == "foo" + my_lambda = -> () { + foo # not guarded + } + + foo = "bar" + + my_lambda() +end + +foos = nil +foos = ["foo"] +bars = NotAnArray.new + +if foos.include?(foo) + foo +else + foo +end + +if bars.include?(foo) + foo +else + foo +end + +if foos.index(foo) != nil + foo +else + foo +end + +if foos.index(foo)r == nil + foo +else + foo +end + +bars = ["bar"] + +if condition + bars = nil +end + +if bars.include?(foo) + foo +else + foo +end diff --git a/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected b/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected index 2d2aa24b2df..76d7b77a729 100644 --- a/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/hash-flow/hash-flow.expected @@ -55,473 +55,481 @@ edges | hash_flow.rb:68:13:68:39 | ...[...] [element :a] : | hash_flow.rb:69:10:69:14 | hash4 [element :a] : | | hash_flow.rb:68:22:68:31 | call to taint : | hash_flow.rb:68:13:68:39 | ...[...] [element :a] : | | hash_flow.rb:69:10:69:14 | hash4 [element :a] : | hash_flow.rb:69:10:69:18 | ...[...] | -| hash_flow.rb:76:13:76:42 | call to [] [element :a] : | hash_flow.rb:77:10:77:14 | hash1 [element :a] : | -| hash_flow.rb:76:26:76:35 | call to taint : | hash_flow.rb:76:13:76:42 | call to [] [element :a] : | -| hash_flow.rb:77:10:77:14 | hash1 [element :a] : | hash_flow.rb:77:10:77:18 | ...[...] | -| hash_flow.rb:85:15:85:24 | call to taint : | hash_flow.rb:88:30:88:33 | hash [element :a] : | -| hash_flow.rb:88:13:88:34 | call to try_convert [element :a] : | hash_flow.rb:89:10:89:14 | hash2 [element :a] : | -| hash_flow.rb:88:30:88:33 | hash [element :a] : | hash_flow.rb:88:13:88:34 | call to try_convert [element :a] : | -| hash_flow.rb:89:10:89:14 | hash2 [element :a] : | hash_flow.rb:89:10:89:18 | ...[...] | -| hash_flow.rb:97:21:97:30 | call to taint : | hash_flow.rb:98:10:98:10 | b | -| hash_flow.rb:105:9:105:12 | [post] hash [element :a] : | hash_flow.rb:106:10:106:13 | hash [element :a] : | -| hash_flow.rb:105:9:105:34 | call to store : | hash_flow.rb:107:10:107:10 | b | -| hash_flow.rb:105:24:105:33 | call to taint : | hash_flow.rb:105:9:105:12 | [post] hash [element :a] : | -| hash_flow.rb:105:24:105:33 | call to taint : | hash_flow.rb:105:9:105:34 | call to store : | -| hash_flow.rb:106:10:106:13 | hash [element :a] : | hash_flow.rb:106:10:106:17 | ...[...] | -| hash_flow.rb:110:9:110:12 | [post] hash [element] : | hash_flow.rb:111:10:111:13 | hash [element] : | -| hash_flow.rb:110:9:110:12 | [post] hash [element] : | hash_flow.rb:112:10:112:13 | hash [element] : | -| hash_flow.rb:110:9:110:33 | call to store : | hash_flow.rb:113:10:113:10 | c | -| hash_flow.rb:110:23:110:32 | call to taint : | hash_flow.rb:110:9:110:12 | [post] hash [element] : | -| hash_flow.rb:110:23:110:32 | call to taint : | hash_flow.rb:110:9:110:33 | call to store : | -| hash_flow.rb:111:10:111:13 | hash [element] : | hash_flow.rb:111:10:111:17 | ...[...] | -| hash_flow.rb:112:10:112:13 | hash [element] : | hash_flow.rb:112:10:112:17 | ...[...] | -| hash_flow.rb:120:15:120:24 | call to taint : | hash_flow.rb:123:5:123:8 | hash [element :a] : | -| hash_flow.rb:120:15:120:24 | call to taint : | hash_flow.rb:126:5:126:8 | hash [element :a] : | -| hash_flow.rb:123:5:123:8 | hash [element :a] : | hash_flow.rb:123:18:123:29 | key_or_value : | -| hash_flow.rb:123:18:123:29 | key_or_value : | hash_flow.rb:124:14:124:25 | key_or_value | -| hash_flow.rb:126:5:126:8 | hash [element :a] : | hash_flow.rb:126:22:126:26 | value : | -| hash_flow.rb:126:22:126:26 | value : | hash_flow.rb:128:14:128:18 | value | -| hash_flow.rb:136:15:136:25 | call to taint : | hash_flow.rb:139:9:139:12 | hash [element :a] : | -| hash_flow.rb:136:15:136:25 | call to taint : | hash_flow.rb:143:9:143:12 | hash [element :a] : | -| hash_flow.rb:139:9:139:12 | hash [element :a] : | hash_flow.rb:139:9:139:22 | call to assoc [element 1] : | -| hash_flow.rb:139:9:139:22 | call to assoc [element 1] : | hash_flow.rb:141:10:141:10 | b [element 1] : | -| hash_flow.rb:139:9:139:22 | call to assoc [element 1] : | hash_flow.rb:142:10:142:10 | b [element 1] : | -| hash_flow.rb:141:10:141:10 | b [element 1] : | hash_flow.rb:141:10:141:13 | ...[...] | -| hash_flow.rb:142:10:142:10 | b [element 1] : | hash_flow.rb:142:10:142:13 | ...[...] | -| hash_flow.rb:143:9:143:12 | hash [element :a] : | hash_flow.rb:143:9:143:21 | call to assoc [element 1] : | -| hash_flow.rb:143:9:143:21 | call to assoc [element 1] : | hash_flow.rb:144:10:144:10 | c [element 1] : | -| hash_flow.rb:144:10:144:10 | c [element 1] : | hash_flow.rb:144:10:144:13 | ...[...] | -| hash_flow.rb:162:15:162:25 | call to taint : | hash_flow.rb:165:9:165:12 | hash [element :a] : | -| hash_flow.rb:165:9:165:12 | hash [element :a] : | hash_flow.rb:165:9:165:20 | call to compact [element :a] : | -| hash_flow.rb:165:9:165:20 | call to compact [element :a] : | hash_flow.rb:166:10:166:10 | a [element :a] : | -| hash_flow.rb:166:10:166:10 | a [element :a] : | hash_flow.rb:166:10:166:14 | ...[...] | -| hash_flow.rb:174:15:174:25 | call to taint : | hash_flow.rb:177:9:177:12 | hash [element :a] : | -| hash_flow.rb:177:9:177:12 | hash [element :a] : | hash_flow.rb:177:9:177:23 | call to delete : | -| hash_flow.rb:177:9:177:23 | call to delete : | hash_flow.rb:178:10:178:10 | a | -| hash_flow.rb:186:15:186:25 | call to taint : | hash_flow.rb:189:9:189:12 | hash [element :a] : | -| hash_flow.rb:186:15:186:25 | call to taint : | hash_flow.rb:194:10:194:13 | hash [element :a] : | -| hash_flow.rb:189:9:189:12 | [post] hash [element :a] : | hash_flow.rb:194:10:194:13 | hash [element :a] : | -| hash_flow.rb:189:9:189:12 | hash [element :a] : | hash_flow.rb:189:9:189:12 | [post] hash [element :a] : | -| hash_flow.rb:189:9:189:12 | hash [element :a] : | hash_flow.rb:189:9:192:7 | call to delete_if [element :a] : | -| hash_flow.rb:189:9:189:12 | hash [element :a] : | hash_flow.rb:189:33:189:37 | value : | -| hash_flow.rb:189:9:192:7 | call to delete_if [element :a] : | hash_flow.rb:193:10:193:10 | a [element :a] : | -| hash_flow.rb:189:33:189:37 | value : | hash_flow.rb:191:14:191:18 | value | -| hash_flow.rb:193:10:193:10 | a [element :a] : | hash_flow.rb:193:10:193:14 | ...[...] | -| hash_flow.rb:194:10:194:13 | hash [element :a] : | hash_flow.rb:194:10:194:17 | ...[...] | -| hash_flow.rb:202:15:202:25 | call to taint : | hash_flow.rb:209:10:209:13 | hash [element :a] : | -| hash_flow.rb:205:19:205:29 | call to taint : | hash_flow.rb:211:10:211:13 | hash [element :c, element :d] : | -| hash_flow.rb:209:10:209:13 | hash [element :a] : | hash_flow.rb:209:10:209:21 | call to dig | -| hash_flow.rb:211:10:211:13 | hash [element :c, element :d] : | hash_flow.rb:211:10:211:24 | call to dig | -| hash_flow.rb:219:15:219:25 | call to taint : | hash_flow.rb:222:9:222:12 | hash [element :a] : | -| hash_flow.rb:222:9:222:12 | hash [element :a] : | hash_flow.rb:222:9:225:7 | call to each [element :a] : | -| hash_flow.rb:222:9:222:12 | hash [element :a] : | hash_flow.rb:222:28:222:32 | value : | -| hash_flow.rb:222:9:225:7 | call to each [element :a] : | hash_flow.rb:226:10:226:10 | x [element :a] : | -| hash_flow.rb:222:28:222:32 | value : | hash_flow.rb:224:14:224:18 | value | -| hash_flow.rb:226:10:226:10 | x [element :a] : | hash_flow.rb:226:10:226:14 | ...[...] | -| hash_flow.rb:234:15:234:25 | call to taint : | hash_flow.rb:237:9:237:12 | hash [element :a] : | -| hash_flow.rb:237:9:237:12 | hash [element :a] : | hash_flow.rb:237:9:239:7 | call to each_key [element :a] : | -| hash_flow.rb:237:9:239:7 | call to each_key [element :a] : | hash_flow.rb:240:10:240:10 | x [element :a] : | -| hash_flow.rb:240:10:240:10 | x [element :a] : | hash_flow.rb:240:10:240:14 | ...[...] | -| hash_flow.rb:248:15:248:25 | call to taint : | hash_flow.rb:251:9:251:12 | hash [element :a] : | -| hash_flow.rb:251:9:251:12 | hash [element :a] : | hash_flow.rb:251:9:254:7 | call to each_pair [element :a] : | -| hash_flow.rb:251:9:251:12 | hash [element :a] : | hash_flow.rb:251:33:251:37 | value : | -| hash_flow.rb:251:9:254:7 | call to each_pair [element :a] : | hash_flow.rb:255:10:255:10 | x [element :a] : | -| hash_flow.rb:251:33:251:37 | value : | hash_flow.rb:253:14:253:18 | value | -| hash_flow.rb:255:10:255:10 | x [element :a] : | hash_flow.rb:255:10:255:14 | ...[...] | -| hash_flow.rb:263:15:263:25 | call to taint : | hash_flow.rb:266:9:266:12 | hash [element :a] : | -| hash_flow.rb:266:9:266:12 | hash [element :a] : | hash_flow.rb:266:9:268:7 | call to each_value [element :a] : | -| hash_flow.rb:266:9:266:12 | hash [element :a] : | hash_flow.rb:266:29:266:33 | value : | -| hash_flow.rb:266:9:268:7 | call to each_value [element :a] : | hash_flow.rb:269:10:269:10 | x [element :a] : | -| hash_flow.rb:266:29:266:33 | value : | hash_flow.rb:267:14:267:18 | value | -| hash_flow.rb:269:10:269:10 | x [element :a] : | hash_flow.rb:269:10:269:14 | ...[...] | -| hash_flow.rb:279:15:279:25 | call to taint : | hash_flow.rb:282:9:282:12 | hash [element :c] : | -| hash_flow.rb:282:9:282:12 | hash [element :c] : | hash_flow.rb:282:9:282:28 | call to except [element :c] : | -| hash_flow.rb:282:9:282:28 | call to except [element :c] : | hash_flow.rb:285:10:285:10 | x [element :c] : | -| hash_flow.rb:285:10:285:10 | x [element :c] : | hash_flow.rb:285:10:285:14 | ...[...] | -| hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:297:9:297:12 | hash [element :a] : | -| hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:301:9:301:12 | hash [element :a] : | -| hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:303:9:303:12 | hash [element :a] : | -| hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:307:9:307:12 | hash [element :a] : | -| hash_flow.rb:295:15:295:25 | call to taint : | hash_flow.rb:297:9:297:12 | hash [element :c] : | -| hash_flow.rb:295:15:295:25 | call to taint : | hash_flow.rb:307:9:307:12 | hash [element :c] : | -| hash_flow.rb:297:9:297:12 | hash [element :a] : | hash_flow.rb:297:9:299:7 | call to fetch : | -| hash_flow.rb:297:9:297:12 | hash [element :c] : | hash_flow.rb:297:9:299:7 | call to fetch : | -| hash_flow.rb:297:9:299:7 | call to fetch : | hash_flow.rb:300:10:300:10 | b | -| hash_flow.rb:297:20:297:30 | call to taint : | hash_flow.rb:297:37:297:37 | x : | -| hash_flow.rb:297:37:297:37 | x : | hash_flow.rb:298:14:298:14 | x | -| hash_flow.rb:301:9:301:12 | hash [element :a] : | hash_flow.rb:301:9:301:22 | call to fetch : | -| hash_flow.rb:301:9:301:22 | call to fetch : | hash_flow.rb:302:10:302:10 | b | -| hash_flow.rb:303:9:303:12 | hash [element :a] : | hash_flow.rb:303:9:303:35 | call to fetch : | -| hash_flow.rb:303:9:303:35 | call to fetch : | hash_flow.rb:304:10:304:10 | b | -| hash_flow.rb:303:24:303:34 | call to taint : | hash_flow.rb:303:9:303:35 | call to fetch : | -| hash_flow.rb:305:9:305:35 | call to fetch : | hash_flow.rb:306:10:306:10 | b | -| hash_flow.rb:305:24:305:34 | call to taint : | hash_flow.rb:305:9:305:35 | call to fetch : | -| hash_flow.rb:307:9:307:12 | hash [element :a] : | hash_flow.rb:307:9:307:34 | call to fetch : | -| hash_flow.rb:307:9:307:12 | hash [element :c] : | hash_flow.rb:307:9:307:34 | call to fetch : | -| hash_flow.rb:307:9:307:34 | call to fetch : | hash_flow.rb:308:10:308:10 | b | -| hash_flow.rb:307:23:307:33 | call to taint : | hash_flow.rb:307:9:307:34 | call to fetch : | -| hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:319:9:319:12 | hash [element :a] : | -| hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:324:9:324:12 | hash [element :a] : | -| hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:326:9:326:12 | hash [element :a] : | -| hash_flow.rb:317:15:317:25 | call to taint : | hash_flow.rb:319:9:319:12 | hash [element :c] : | -| hash_flow.rb:317:15:317:25 | call to taint : | hash_flow.rb:326:9:326:12 | hash [element :c] : | -| hash_flow.rb:319:9:319:12 | hash [element :a] : | hash_flow.rb:319:9:322:7 | call to fetch_values [element] : | -| hash_flow.rb:319:9:319:12 | hash [element :c] : | hash_flow.rb:319:9:322:7 | call to fetch_values [element] : | -| hash_flow.rb:319:9:322:7 | call to fetch_values [element] : | hash_flow.rb:323:10:323:10 | b [element] : | -| hash_flow.rb:319:27:319:37 | call to taint : | hash_flow.rb:319:44:319:44 | x : | -| hash_flow.rb:319:44:319:44 | x : | hash_flow.rb:320:14:320:14 | x | -| hash_flow.rb:321:9:321:19 | call to taint : | hash_flow.rb:319:9:322:7 | call to fetch_values [element] : | -| hash_flow.rb:323:10:323:10 | b [element] : | hash_flow.rb:323:10:323:13 | ...[...] | -| hash_flow.rb:324:9:324:12 | hash [element :a] : | hash_flow.rb:324:9:324:29 | call to fetch_values [element] : | -| hash_flow.rb:324:9:324:29 | call to fetch_values [element] : | hash_flow.rb:325:10:325:10 | b [element] : | -| hash_flow.rb:325:10:325:10 | b [element] : | hash_flow.rb:325:10:325:13 | ...[...] | -| hash_flow.rb:326:9:326:12 | hash [element :a] : | hash_flow.rb:326:9:326:31 | call to fetch_values [element] : | -| hash_flow.rb:326:9:326:12 | hash [element :c] : | hash_flow.rb:326:9:326:31 | call to fetch_values [element] : | -| hash_flow.rb:326:9:326:31 | call to fetch_values [element] : | hash_flow.rb:327:10:327:10 | b [element] : | -| hash_flow.rb:327:10:327:10 | b [element] : | hash_flow.rb:327:10:327:13 | ...[...] | -| hash_flow.rb:334:15:334:25 | call to taint : | hash_flow.rb:338:9:338:12 | hash [element :a] : | -| hash_flow.rb:336:15:336:25 | call to taint : | hash_flow.rb:338:9:338:12 | hash [element :c] : | -| hash_flow.rb:338:9:338:12 | hash [element :a] : | hash_flow.rb:338:9:342:7 | call to filter [element :a] : | -| hash_flow.rb:338:9:338:12 | hash [element :a] : | hash_flow.rb:338:30:338:34 | value : | -| hash_flow.rb:338:9:338:12 | hash [element :c] : | hash_flow.rb:338:30:338:34 | value : | -| hash_flow.rb:338:9:342:7 | call to filter [element :a] : | hash_flow.rb:343:11:343:11 | b [element :a] : | -| hash_flow.rb:338:30:338:34 | value : | hash_flow.rb:340:14:340:18 | value | -| hash_flow.rb:343:11:343:11 | b [element :a] : | hash_flow.rb:343:11:343:15 | ...[...] : | -| hash_flow.rb:343:11:343:15 | ...[...] : | hash_flow.rb:343:10:343:16 | ( ... ) | -| hash_flow.rb:350:15:350:25 | call to taint : | hash_flow.rb:354:5:354:8 | hash [element :a] : | -| hash_flow.rb:352:15:352:25 | call to taint : | hash_flow.rb:354:5:354:8 | hash [element :c] : | -| hash_flow.rb:354:5:354:8 | [post] hash [element :a] : | hash_flow.rb:359:11:359:14 | hash [element :a] : | -| hash_flow.rb:354:5:354:8 | hash [element :a] : | hash_flow.rb:354:5:354:8 | [post] hash [element :a] : | -| hash_flow.rb:354:5:354:8 | hash [element :a] : | hash_flow.rb:354:27:354:31 | value : | -| hash_flow.rb:354:5:354:8 | hash [element :c] : | hash_flow.rb:354:27:354:31 | value : | -| hash_flow.rb:354:27:354:31 | value : | hash_flow.rb:356:14:356:18 | value | -| hash_flow.rb:359:11:359:14 | hash [element :a] : | hash_flow.rb:359:11:359:18 | ...[...] : | -| hash_flow.rb:359:11:359:18 | ...[...] : | hash_flow.rb:359:10:359:19 | ( ... ) | -| hash_flow.rb:366:15:366:25 | call to taint : | hash_flow.rb:370:9:370:12 | hash [element :a] : | -| hash_flow.rb:368:15:368:25 | call to taint : | hash_flow.rb:370:9:370:12 | hash [element :c] : | -| hash_flow.rb:370:9:370:12 | hash [element :a] : | hash_flow.rb:370:9:370:20 | call to flatten [element] : | -| hash_flow.rb:370:9:370:12 | hash [element :c] : | hash_flow.rb:370:9:370:20 | call to flatten [element] : | -| hash_flow.rb:370:9:370:20 | call to flatten [element] : | hash_flow.rb:371:11:371:11 | b [element] : | -| hash_flow.rb:371:11:371:11 | b [element] : | hash_flow.rb:371:11:371:14 | ...[...] : | -| hash_flow.rb:371:11:371:14 | ...[...] : | hash_flow.rb:371:10:371:15 | ( ... ) | -| hash_flow.rb:378:15:378:25 | call to taint : | hash_flow.rb:382:9:382:12 | hash [element :a] : | -| hash_flow.rb:380:15:380:25 | call to taint : | hash_flow.rb:382:9:382:12 | hash [element :c] : | -| hash_flow.rb:382:9:382:12 | [post] hash [element :a] : | hash_flow.rb:387:11:387:14 | hash [element :a] : | -| hash_flow.rb:382:9:382:12 | hash [element :a] : | hash_flow.rb:382:9:382:12 | [post] hash [element :a] : | -| hash_flow.rb:382:9:382:12 | hash [element :a] : | hash_flow.rb:382:9:386:7 | call to keep_if [element :a] : | -| hash_flow.rb:382:9:382:12 | hash [element :a] : | hash_flow.rb:382:31:382:35 | value : | -| hash_flow.rb:382:9:382:12 | hash [element :c] : | hash_flow.rb:382:31:382:35 | value : | -| hash_flow.rb:382:9:386:7 | call to keep_if [element :a] : | hash_flow.rb:388:11:388:11 | b [element :a] : | -| hash_flow.rb:382:31:382:35 | value : | hash_flow.rb:384:14:384:18 | value | -| hash_flow.rb:387:11:387:14 | hash [element :a] : | hash_flow.rb:387:11:387:18 | ...[...] : | -| hash_flow.rb:387:11:387:18 | ...[...] : | hash_flow.rb:387:10:387:19 | ( ... ) | -| hash_flow.rb:388:11:388:11 | b [element :a] : | hash_flow.rb:388:11:388:15 | ...[...] : | -| hash_flow.rb:388:11:388:15 | ...[...] : | hash_flow.rb:388:10:388:16 | ( ... ) | -| hash_flow.rb:395:15:395:25 | call to taint : | hash_flow.rb:404:12:404:16 | hash1 [element :a] : | -| hash_flow.rb:397:15:397:25 | call to taint : | hash_flow.rb:404:12:404:16 | hash1 [element :c] : | -| hash_flow.rb:400:15:400:25 | call to taint : | hash_flow.rb:404:24:404:28 | hash2 [element :d] : | -| hash_flow.rb:402:15:402:25 | call to taint : | hash_flow.rb:404:24:404:28 | hash2 [element :f] : | -| hash_flow.rb:404:12:404:16 | hash1 [element :a] : | hash_flow.rb:404:12:408:7 | call to merge [element :a] : | -| hash_flow.rb:404:12:404:16 | hash1 [element :a] : | hash_flow.rb:404:40:404:48 | old_value : | -| hash_flow.rb:404:12:404:16 | hash1 [element :a] : | hash_flow.rb:404:51:404:59 | new_value : | -| hash_flow.rb:404:12:404:16 | hash1 [element :c] : | hash_flow.rb:404:12:408:7 | call to merge [element :c] : | -| hash_flow.rb:404:12:404:16 | hash1 [element :c] : | hash_flow.rb:404:40:404:48 | old_value : | -| hash_flow.rb:404:12:404:16 | hash1 [element :c] : | hash_flow.rb:404:51:404:59 | new_value : | -| hash_flow.rb:404:12:408:7 | call to merge [element :a] : | hash_flow.rb:409:11:409:14 | hash [element :a] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :c] : | hash_flow.rb:411:11:411:14 | hash [element :c] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :d] : | hash_flow.rb:412:11:412:14 | hash [element :d] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :f] : | hash_flow.rb:414:11:414:14 | hash [element :f] : | -| hash_flow.rb:404:24:404:28 | hash2 [element :d] : | hash_flow.rb:404:12:408:7 | call to merge [element :d] : | -| hash_flow.rb:404:24:404:28 | hash2 [element :d] : | hash_flow.rb:404:40:404:48 | old_value : | -| hash_flow.rb:404:24:404:28 | hash2 [element :d] : | hash_flow.rb:404:51:404:59 | new_value : | -| hash_flow.rb:404:24:404:28 | hash2 [element :f] : | hash_flow.rb:404:12:408:7 | call to merge [element :f] : | -| hash_flow.rb:404:24:404:28 | hash2 [element :f] : | hash_flow.rb:404:40:404:48 | old_value : | -| hash_flow.rb:404:24:404:28 | hash2 [element :f] : | hash_flow.rb:404:51:404:59 | new_value : | -| hash_flow.rb:404:40:404:48 | old_value : | hash_flow.rb:406:14:406:22 | old_value | -| hash_flow.rb:404:51:404:59 | new_value : | hash_flow.rb:407:14:407:22 | new_value | -| hash_flow.rb:409:11:409:14 | hash [element :a] : | hash_flow.rb:409:11:409:18 | ...[...] : | -| hash_flow.rb:409:11:409:18 | ...[...] : | hash_flow.rb:409:10:409:19 | ( ... ) | -| hash_flow.rb:411:11:411:14 | hash [element :c] : | hash_flow.rb:411:11:411:18 | ...[...] : | -| hash_flow.rb:411:11:411:18 | ...[...] : | hash_flow.rb:411:10:411:19 | ( ... ) | -| hash_flow.rb:412:11:412:14 | hash [element :d] : | hash_flow.rb:412:11:412:18 | ...[...] : | -| hash_flow.rb:412:11:412:18 | ...[...] : | hash_flow.rb:412:10:412:19 | ( ... ) | -| hash_flow.rb:414:11:414:14 | hash [element :f] : | hash_flow.rb:414:11:414:18 | ...[...] : | -| hash_flow.rb:414:11:414:18 | ...[...] : | hash_flow.rb:414:10:414:19 | ( ... ) | -| hash_flow.rb:421:15:421:25 | call to taint : | hash_flow.rb:430:12:430:16 | hash1 [element :a] : | -| hash_flow.rb:423:15:423:25 | call to taint : | hash_flow.rb:430:12:430:16 | hash1 [element :c] : | -| hash_flow.rb:426:15:426:25 | call to taint : | hash_flow.rb:430:25:430:29 | hash2 [element :d] : | -| hash_flow.rb:428:15:428:25 | call to taint : | hash_flow.rb:430:25:430:29 | hash2 [element :f] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :a] : | hash_flow.rb:442:11:442:15 | hash1 [element :a] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :c] : | hash_flow.rb:444:11:444:15 | hash1 [element :c] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :d] : | hash_flow.rb:445:11:445:15 | hash1 [element :d] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :f] : | hash_flow.rb:447:11:447:15 | hash1 [element :f] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :a] : | hash_flow.rb:430:12:430:16 | [post] hash1 [element :a] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :a] : | hash_flow.rb:430:12:434:7 | call to merge! [element :a] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :a] : | hash_flow.rb:430:41:430:49 | old_value : | -| hash_flow.rb:430:12:430:16 | hash1 [element :a] : | hash_flow.rb:430:52:430:60 | new_value : | -| hash_flow.rb:430:12:430:16 | hash1 [element :c] : | hash_flow.rb:430:12:430:16 | [post] hash1 [element :c] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :c] : | hash_flow.rb:430:12:434:7 | call to merge! [element :c] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :c] : | hash_flow.rb:430:41:430:49 | old_value : | -| hash_flow.rb:430:12:430:16 | hash1 [element :c] : | hash_flow.rb:430:52:430:60 | new_value : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :a] : | hash_flow.rb:435:11:435:14 | hash [element :a] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :c] : | hash_flow.rb:437:11:437:14 | hash [element :c] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :d] : | hash_flow.rb:438:11:438:14 | hash [element :d] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :f] : | hash_flow.rb:440:11:440:14 | hash [element :f] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :d] : | hash_flow.rb:430:12:430:16 | [post] hash1 [element :d] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :d] : | hash_flow.rb:430:12:434:7 | call to merge! [element :d] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :d] : | hash_flow.rb:430:41:430:49 | old_value : | -| hash_flow.rb:430:25:430:29 | hash2 [element :d] : | hash_flow.rb:430:52:430:60 | new_value : | -| hash_flow.rb:430:25:430:29 | hash2 [element :f] : | hash_flow.rb:430:12:430:16 | [post] hash1 [element :f] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :f] : | hash_flow.rb:430:12:434:7 | call to merge! [element :f] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :f] : | hash_flow.rb:430:41:430:49 | old_value : | -| hash_flow.rb:430:25:430:29 | hash2 [element :f] : | hash_flow.rb:430:52:430:60 | new_value : | -| hash_flow.rb:430:41:430:49 | old_value : | hash_flow.rb:432:14:432:22 | old_value | -| hash_flow.rb:430:52:430:60 | new_value : | hash_flow.rb:433:14:433:22 | new_value | -| hash_flow.rb:435:11:435:14 | hash [element :a] : | hash_flow.rb:435:11:435:18 | ...[...] : | -| hash_flow.rb:435:11:435:18 | ...[...] : | hash_flow.rb:435:10:435:19 | ( ... ) | -| hash_flow.rb:437:11:437:14 | hash [element :c] : | hash_flow.rb:437:11:437:18 | ...[...] : | -| hash_flow.rb:437:11:437:18 | ...[...] : | hash_flow.rb:437:10:437:19 | ( ... ) | -| hash_flow.rb:438:11:438:14 | hash [element :d] : | hash_flow.rb:438:11:438:18 | ...[...] : | -| hash_flow.rb:438:11:438:18 | ...[...] : | hash_flow.rb:438:10:438:19 | ( ... ) | -| hash_flow.rb:440:11:440:14 | hash [element :f] : | hash_flow.rb:440:11:440:18 | ...[...] : | -| hash_flow.rb:440:11:440:18 | ...[...] : | hash_flow.rb:440:10:440:19 | ( ... ) | -| hash_flow.rb:442:11:442:15 | hash1 [element :a] : | hash_flow.rb:442:11:442:19 | ...[...] : | -| hash_flow.rb:442:11:442:19 | ...[...] : | hash_flow.rb:442:10:442:20 | ( ... ) | -| hash_flow.rb:444:11:444:15 | hash1 [element :c] : | hash_flow.rb:444:11:444:19 | ...[...] : | -| hash_flow.rb:444:11:444:19 | ...[...] : | hash_flow.rb:444:10:444:20 | ( ... ) | -| hash_flow.rb:445:11:445:15 | hash1 [element :d] : | hash_flow.rb:445:11:445:19 | ...[...] : | -| hash_flow.rb:445:11:445:19 | ...[...] : | hash_flow.rb:445:10:445:20 | ( ... ) | -| hash_flow.rb:447:11:447:15 | hash1 [element :f] : | hash_flow.rb:447:11:447:19 | ...[...] : | -| hash_flow.rb:447:11:447:19 | ...[...] : | hash_flow.rb:447:10:447:20 | ( ... ) | -| hash_flow.rb:454:15:454:25 | call to taint : | hash_flow.rb:457:9:457:12 | hash [element :a] : | -| hash_flow.rb:457:9:457:12 | hash [element :a] : | hash_flow.rb:457:9:457:22 | call to rassoc [element 1] : | -| hash_flow.rb:457:9:457:22 | call to rassoc [element 1] : | hash_flow.rb:459:10:459:10 | b [element 1] : | -| hash_flow.rb:459:10:459:10 | b [element 1] : | hash_flow.rb:459:10:459:13 | ...[...] | -| hash_flow.rb:466:15:466:25 | call to taint : | hash_flow.rb:469:9:469:12 | hash [element :a] : | -| hash_flow.rb:469:9:469:12 | hash [element :a] : | hash_flow.rb:469:9:473:7 | call to reject [element :a] : | -| hash_flow.rb:469:9:469:12 | hash [element :a] : | hash_flow.rb:469:29:469:33 | value : | -| hash_flow.rb:469:9:473:7 | call to reject [element :a] : | hash_flow.rb:474:10:474:10 | b [element :a] : | -| hash_flow.rb:469:29:469:33 | value : | hash_flow.rb:471:14:471:18 | value | -| hash_flow.rb:474:10:474:10 | b [element :a] : | hash_flow.rb:474:10:474:14 | ...[...] | -| hash_flow.rb:481:15:481:25 | call to taint : | hash_flow.rb:484:9:484:12 | hash [element :a] : | -| hash_flow.rb:481:15:481:25 | call to taint : | hash_flow.rb:490:10:490:13 | hash [element :a] : | -| hash_flow.rb:484:9:484:12 | [post] hash [element :a] : | hash_flow.rb:490:10:490:13 | hash [element :a] : | -| hash_flow.rb:484:9:484:12 | hash [element :a] : | hash_flow.rb:484:9:484:12 | [post] hash [element :a] : | -| hash_flow.rb:484:9:484:12 | hash [element :a] : | hash_flow.rb:484:9:488:7 | call to reject! [element :a] : | -| hash_flow.rb:484:9:484:12 | hash [element :a] : | hash_flow.rb:484:30:484:34 | value : | -| hash_flow.rb:484:9:488:7 | call to reject! [element :a] : | hash_flow.rb:489:10:489:10 | b [element :a] : | -| hash_flow.rb:484:30:484:34 | value : | hash_flow.rb:486:14:486:18 | value | -| hash_flow.rb:489:10:489:10 | b [element :a] : | hash_flow.rb:489:10:489:14 | ...[...] | -| hash_flow.rb:490:10:490:13 | hash [element :a] : | hash_flow.rb:490:10:490:17 | ...[...] | -| hash_flow.rb:497:15:497:25 | call to taint : | hash_flow.rb:504:19:504:22 | hash [element :a] : | -| hash_flow.rb:499:15:499:25 | call to taint : | hash_flow.rb:504:19:504:22 | hash [element :c] : | -| hash_flow.rb:504:5:504:9 | [post] hash2 [element :a] : | hash_flow.rb:505:11:505:15 | hash2 [element :a] : | -| hash_flow.rb:504:5:504:9 | [post] hash2 [element :c] : | hash_flow.rb:507:11:507:15 | hash2 [element :c] : | -| hash_flow.rb:504:19:504:22 | hash [element :a] : | hash_flow.rb:504:5:504:9 | [post] hash2 [element :a] : | -| hash_flow.rb:504:19:504:22 | hash [element :c] : | hash_flow.rb:504:5:504:9 | [post] hash2 [element :c] : | -| hash_flow.rb:505:11:505:15 | hash2 [element :a] : | hash_flow.rb:505:11:505:19 | ...[...] : | -| hash_flow.rb:505:11:505:19 | ...[...] : | hash_flow.rb:505:10:505:20 | ( ... ) | -| hash_flow.rb:507:11:507:15 | hash2 [element :c] : | hash_flow.rb:507:11:507:19 | ...[...] : | -| hash_flow.rb:507:11:507:19 | ...[...] : | hash_flow.rb:507:10:507:20 | ( ... ) | -| hash_flow.rb:512:15:512:25 | call to taint : | hash_flow.rb:516:9:516:12 | hash [element :a] : | -| hash_flow.rb:514:15:514:25 | call to taint : | hash_flow.rb:516:9:516:12 | hash [element :c] : | -| hash_flow.rb:516:9:516:12 | hash [element :a] : | hash_flow.rb:516:9:520:7 | call to select [element :a] : | -| hash_flow.rb:516:9:516:12 | hash [element :a] : | hash_flow.rb:516:30:516:34 | value : | -| hash_flow.rb:516:9:516:12 | hash [element :c] : | hash_flow.rb:516:30:516:34 | value : | -| hash_flow.rb:516:9:520:7 | call to select [element :a] : | hash_flow.rb:521:11:521:11 | b [element :a] : | -| hash_flow.rb:516:30:516:34 | value : | hash_flow.rb:518:14:518:18 | value | -| hash_flow.rb:521:11:521:11 | b [element :a] : | hash_flow.rb:521:11:521:15 | ...[...] : | -| hash_flow.rb:521:11:521:15 | ...[...] : | hash_flow.rb:521:10:521:16 | ( ... ) | -| hash_flow.rb:528:15:528:25 | call to taint : | hash_flow.rb:532:5:532:8 | hash [element :a] : | -| hash_flow.rb:530:15:530:25 | call to taint : | hash_flow.rb:532:5:532:8 | hash [element :c] : | -| hash_flow.rb:532:5:532:8 | [post] hash [element :a] : | hash_flow.rb:537:11:537:14 | hash [element :a] : | -| hash_flow.rb:532:5:532:8 | hash [element :a] : | hash_flow.rb:532:5:532:8 | [post] hash [element :a] : | -| hash_flow.rb:532:5:532:8 | hash [element :a] : | hash_flow.rb:532:27:532:31 | value : | -| hash_flow.rb:532:5:532:8 | hash [element :c] : | hash_flow.rb:532:27:532:31 | value : | -| hash_flow.rb:532:27:532:31 | value : | hash_flow.rb:534:14:534:18 | value | -| hash_flow.rb:537:11:537:14 | hash [element :a] : | hash_flow.rb:537:11:537:18 | ...[...] : | -| hash_flow.rb:537:11:537:18 | ...[...] : | hash_flow.rb:537:10:537:19 | ( ... ) | -| hash_flow.rb:544:15:544:25 | call to taint : | hash_flow.rb:548:9:548:12 | hash [element :a] : | -| hash_flow.rb:546:15:546:25 | call to taint : | hash_flow.rb:548:9:548:12 | hash [element :c] : | -| hash_flow.rb:548:9:548:12 | [post] hash [element :a] : | hash_flow.rb:549:11:549:14 | hash [element :a] : | -| hash_flow.rb:548:9:548:12 | hash [element :a] : | hash_flow.rb:548:9:548:12 | [post] hash [element :a] : | -| hash_flow.rb:548:9:548:12 | hash [element :a] : | hash_flow.rb:548:9:548:18 | call to shift [element 1] : | -| hash_flow.rb:548:9:548:12 | hash [element :c] : | hash_flow.rb:548:9:548:18 | call to shift [element 1] : | -| hash_flow.rb:548:9:548:18 | call to shift [element 1] : | hash_flow.rb:551:11:551:11 | b [element 1] : | -| hash_flow.rb:549:11:549:14 | hash [element :a] : | hash_flow.rb:549:11:549:18 | ...[...] : | -| hash_flow.rb:549:11:549:18 | ...[...] : | hash_flow.rb:549:10:549:19 | ( ... ) | -| hash_flow.rb:551:11:551:11 | b [element 1] : | hash_flow.rb:551:11:551:14 | ...[...] : | -| hash_flow.rb:551:11:551:14 | ...[...] : | hash_flow.rb:551:10:551:15 | ( ... ) | -| hash_flow.rb:558:15:558:25 | call to taint : | hash_flow.rb:562:9:562:12 | hash [element :a] : | -| hash_flow.rb:558:15:558:25 | call to taint : | hash_flow.rb:567:9:567:12 | hash [element :a] : | -| hash_flow.rb:560:15:560:25 | call to taint : | hash_flow.rb:567:9:567:12 | hash [element :c] : | -| hash_flow.rb:562:9:562:12 | hash [element :a] : | hash_flow.rb:562:9:562:26 | call to slice [element :a] : | -| hash_flow.rb:562:9:562:26 | call to slice [element :a] : | hash_flow.rb:563:11:563:11 | b [element :a] : | -| hash_flow.rb:563:11:563:11 | b [element :a] : | hash_flow.rb:563:11:563:15 | ...[...] : | -| hash_flow.rb:563:11:563:15 | ...[...] : | hash_flow.rb:563:10:563:16 | ( ... ) | -| hash_flow.rb:567:9:567:12 | hash [element :a] : | hash_flow.rb:567:9:567:25 | call to slice [element :a] : | -| hash_flow.rb:567:9:567:12 | hash [element :c] : | hash_flow.rb:567:9:567:25 | call to slice [element :c] : | -| hash_flow.rb:567:9:567:25 | call to slice [element :a] : | hash_flow.rb:568:11:568:11 | c [element :a] : | -| hash_flow.rb:567:9:567:25 | call to slice [element :c] : | hash_flow.rb:570:11:570:11 | c [element :c] : | -| hash_flow.rb:568:11:568:11 | c [element :a] : | hash_flow.rb:568:11:568:15 | ...[...] : | -| hash_flow.rb:568:11:568:15 | ...[...] : | hash_flow.rb:568:10:568:16 | ( ... ) | -| hash_flow.rb:570:11:570:11 | c [element :c] : | hash_flow.rb:570:11:570:15 | ...[...] : | -| hash_flow.rb:570:11:570:15 | ...[...] : | hash_flow.rb:570:10:570:16 | ( ... ) | -| hash_flow.rb:577:15:577:25 | call to taint : | hash_flow.rb:581:9:581:12 | hash [element :a] : | -| hash_flow.rb:579:15:579:25 | call to taint : | hash_flow.rb:581:9:581:12 | hash [element :c] : | -| hash_flow.rb:581:9:581:12 | hash [element :a] : | hash_flow.rb:581:9:581:17 | call to to_a [element, element 1] : | -| hash_flow.rb:581:9:581:12 | hash [element :c] : | hash_flow.rb:581:9:581:17 | call to to_a [element, element 1] : | -| hash_flow.rb:581:9:581:17 | call to to_a [element, element 1] : | hash_flow.rb:583:11:583:11 | a [element, element 1] : | -| hash_flow.rb:583:11:583:11 | a [element, element 1] : | hash_flow.rb:583:11:583:14 | ...[...] [element 1] : | -| hash_flow.rb:583:11:583:14 | ...[...] [element 1] : | hash_flow.rb:583:11:583:17 | ...[...] : | -| hash_flow.rb:583:11:583:17 | ...[...] : | hash_flow.rb:583:10:583:18 | ( ... ) | -| hash_flow.rb:590:15:590:25 | call to taint : | hash_flow.rb:594:9:594:12 | hash [element :a] : | -| hash_flow.rb:590:15:590:25 | call to taint : | hash_flow.rb:599:9:599:12 | hash [element :a] : | -| hash_flow.rb:592:15:592:25 | call to taint : | hash_flow.rb:594:9:594:12 | hash [element :c] : | -| hash_flow.rb:592:15:592:25 | call to taint : | hash_flow.rb:599:9:599:12 | hash [element :c] : | -| hash_flow.rb:594:9:594:12 | hash [element :a] : | hash_flow.rb:594:9:594:17 | call to to_h [element :a] : | -| hash_flow.rb:594:9:594:12 | hash [element :c] : | hash_flow.rb:594:9:594:17 | call to to_h [element :c] : | -| hash_flow.rb:594:9:594:17 | call to to_h [element :a] : | hash_flow.rb:595:11:595:11 | a [element :a] : | -| hash_flow.rb:594:9:594:17 | call to to_h [element :c] : | hash_flow.rb:597:11:597:11 | a [element :c] : | -| hash_flow.rb:595:11:595:11 | a [element :a] : | hash_flow.rb:595:11:595:15 | ...[...] : | -| hash_flow.rb:595:11:595:15 | ...[...] : | hash_flow.rb:595:10:595:16 | ( ... ) | -| hash_flow.rb:597:11:597:11 | a [element :c] : | hash_flow.rb:597:11:597:15 | ...[...] : | -| hash_flow.rb:597:11:597:15 | ...[...] : | hash_flow.rb:597:10:597:16 | ( ... ) | -| hash_flow.rb:599:9:599:12 | hash [element :a] : | hash_flow.rb:599:28:599:32 | value : | -| hash_flow.rb:599:9:599:12 | hash [element :c] : | hash_flow.rb:599:28:599:32 | value : | -| hash_flow.rb:599:9:603:7 | call to to_h [element] : | hash_flow.rb:604:11:604:11 | b [element] : | -| hash_flow.rb:599:28:599:32 | value : | hash_flow.rb:601:14:601:18 | value | -| hash_flow.rb:602:14:602:24 | call to taint : | hash_flow.rb:599:9:603:7 | call to to_h [element] : | -| hash_flow.rb:604:11:604:11 | b [element] : | hash_flow.rb:604:11:604:15 | ...[...] : | -| hash_flow.rb:604:11:604:15 | ...[...] : | hash_flow.rb:604:10:604:16 | ( ... ) | -| hash_flow.rb:611:15:611:25 | call to taint : | hash_flow.rb:615:9:615:12 | hash [element :a] : | -| hash_flow.rb:613:15:613:25 | call to taint : | hash_flow.rb:615:9:615:12 | hash [element :c] : | -| hash_flow.rb:615:9:615:12 | hash [element :a] : | hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | -| hash_flow.rb:615:9:615:12 | hash [element :c] : | hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | -| hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | hash_flow.rb:616:11:616:11 | a [element] : | -| hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | hash_flow.rb:617:11:617:11 | a [element] : | -| hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | hash_flow.rb:618:11:618:11 | a [element] : | -| hash_flow.rb:616:11:616:11 | a [element] : | hash_flow.rb:616:11:616:16 | ...[...] : | -| hash_flow.rb:616:11:616:16 | ...[...] : | hash_flow.rb:616:10:616:17 | ( ... ) | -| hash_flow.rb:617:11:617:11 | a [element] : | hash_flow.rb:617:11:617:16 | ...[...] : | -| hash_flow.rb:617:11:617:16 | ...[...] : | hash_flow.rb:617:10:617:17 | ( ... ) | -| hash_flow.rb:618:11:618:11 | a [element] : | hash_flow.rb:618:11:618:16 | ...[...] : | -| hash_flow.rb:618:11:618:16 | ...[...] : | hash_flow.rb:618:10:618:17 | ( ... ) | -| hash_flow.rb:625:15:625:25 | call to taint : | hash_flow.rb:629:5:629:8 | hash [element :a] : | -| hash_flow.rb:627:15:627:25 | call to taint : | hash_flow.rb:629:5:629:8 | hash [element :c] : | -| hash_flow.rb:629:5:629:8 | [post] hash [element] : | hash_flow.rb:630:11:630:14 | hash [element] : | -| hash_flow.rb:629:5:629:8 | [post] hash [element] : | hash_flow.rb:631:11:631:14 | hash [element] : | -| hash_flow.rb:629:5:629:8 | [post] hash [element] : | hash_flow.rb:632:11:632:14 | hash [element] : | -| hash_flow.rb:629:5:629:8 | hash [element :a] : | hash_flow.rb:629:5:629:8 | [post] hash [element] : | -| hash_flow.rb:629:5:629:8 | hash [element :c] : | hash_flow.rb:629:5:629:8 | [post] hash [element] : | -| hash_flow.rb:630:11:630:14 | hash [element] : | hash_flow.rb:630:11:630:19 | ...[...] : | -| hash_flow.rb:630:11:630:19 | ...[...] : | hash_flow.rb:630:10:630:20 | ( ... ) | -| hash_flow.rb:631:11:631:14 | hash [element] : | hash_flow.rb:631:11:631:19 | ...[...] : | -| hash_flow.rb:631:11:631:19 | ...[...] : | hash_flow.rb:631:10:631:20 | ( ... ) | -| hash_flow.rb:632:11:632:14 | hash [element] : | hash_flow.rb:632:11:632:19 | ...[...] : | -| hash_flow.rb:632:11:632:19 | ...[...] : | hash_flow.rb:632:10:632:20 | ( ... ) | -| hash_flow.rb:639:15:639:25 | call to taint : | hash_flow.rb:643:9:643:12 | hash [element :a] : | -| hash_flow.rb:639:15:639:25 | call to taint : | hash_flow.rb:647:11:647:14 | hash [element :a] : | -| hash_flow.rb:641:15:641:25 | call to taint : | hash_flow.rb:643:9:643:12 | hash [element :c] : | -| hash_flow.rb:643:9:643:12 | hash [element :a] : | hash_flow.rb:643:35:643:39 | value : | -| hash_flow.rb:643:9:643:12 | hash [element :c] : | hash_flow.rb:643:35:643:39 | value : | -| hash_flow.rb:643:9:646:7 | call to transform_values [element] : | hash_flow.rb:648:11:648:11 | b [element] : | -| hash_flow.rb:643:35:643:39 | value : | hash_flow.rb:644:14:644:18 | value | -| hash_flow.rb:645:9:645:19 | call to taint : | hash_flow.rb:643:9:646:7 | call to transform_values [element] : | -| hash_flow.rb:647:11:647:14 | hash [element :a] : | hash_flow.rb:647:11:647:18 | ...[...] : | -| hash_flow.rb:647:11:647:18 | ...[...] : | hash_flow.rb:647:10:647:19 | ( ... ) | -| hash_flow.rb:648:11:648:11 | b [element] : | hash_flow.rb:648:11:648:15 | ...[...] : | -| hash_flow.rb:648:11:648:15 | ...[...] : | hash_flow.rb:648:10:648:16 | ( ... ) | -| hash_flow.rb:655:15:655:25 | call to taint : | hash_flow.rb:659:5:659:8 | hash [element :a] : | -| hash_flow.rb:657:15:657:25 | call to taint : | hash_flow.rb:659:5:659:8 | hash [element :c] : | -| hash_flow.rb:659:5:659:8 | [post] hash [element] : | hash_flow.rb:663:11:663:14 | hash [element] : | -| hash_flow.rb:659:5:659:8 | hash [element :a] : | hash_flow.rb:659:32:659:36 | value : | -| hash_flow.rb:659:5:659:8 | hash [element :c] : | hash_flow.rb:659:32:659:36 | value : | -| hash_flow.rb:659:32:659:36 | value : | hash_flow.rb:660:14:660:18 | value | -| hash_flow.rb:661:9:661:19 | call to taint : | hash_flow.rb:659:5:659:8 | [post] hash [element] : | -| hash_flow.rb:663:11:663:14 | hash [element] : | hash_flow.rb:663:11:663:18 | ...[...] : | -| hash_flow.rb:663:11:663:18 | ...[...] : | hash_flow.rb:663:10:663:19 | ( ... ) | -| hash_flow.rb:670:15:670:25 | call to taint : | hash_flow.rb:679:12:679:16 | hash1 [element :a] : | -| hash_flow.rb:672:15:672:25 | call to taint : | hash_flow.rb:679:12:679:16 | hash1 [element :c] : | -| hash_flow.rb:675:15:675:25 | call to taint : | hash_flow.rb:679:25:679:29 | hash2 [element :d] : | -| hash_flow.rb:677:15:677:25 | call to taint : | hash_flow.rb:679:25:679:29 | hash2 [element :f] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :a] : | hash_flow.rb:691:11:691:15 | hash1 [element :a] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :c] : | hash_flow.rb:693:11:693:15 | hash1 [element :c] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :d] : | hash_flow.rb:694:11:694:15 | hash1 [element :d] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :f] : | hash_flow.rb:696:11:696:15 | hash1 [element :f] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :a] : | hash_flow.rb:679:12:679:16 | [post] hash1 [element :a] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :a] : | hash_flow.rb:679:12:683:7 | call to update [element :a] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :a] : | hash_flow.rb:679:41:679:49 | old_value : | -| hash_flow.rb:679:12:679:16 | hash1 [element :a] : | hash_flow.rb:679:52:679:60 | new_value : | -| hash_flow.rb:679:12:679:16 | hash1 [element :c] : | hash_flow.rb:679:12:679:16 | [post] hash1 [element :c] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :c] : | hash_flow.rb:679:12:683:7 | call to update [element :c] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :c] : | hash_flow.rb:679:41:679:49 | old_value : | -| hash_flow.rb:679:12:679:16 | hash1 [element :c] : | hash_flow.rb:679:52:679:60 | new_value : | -| hash_flow.rb:679:12:683:7 | call to update [element :a] : | hash_flow.rb:684:11:684:14 | hash [element :a] : | -| hash_flow.rb:679:12:683:7 | call to update [element :c] : | hash_flow.rb:686:11:686:14 | hash [element :c] : | -| hash_flow.rb:679:12:683:7 | call to update [element :d] : | hash_flow.rb:687:11:687:14 | hash [element :d] : | -| hash_flow.rb:679:12:683:7 | call to update [element :f] : | hash_flow.rb:689:11:689:14 | hash [element :f] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :d] : | hash_flow.rb:679:12:679:16 | [post] hash1 [element :d] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :d] : | hash_flow.rb:679:12:683:7 | call to update [element :d] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :d] : | hash_flow.rb:679:41:679:49 | old_value : | -| hash_flow.rb:679:25:679:29 | hash2 [element :d] : | hash_flow.rb:679:52:679:60 | new_value : | -| hash_flow.rb:679:25:679:29 | hash2 [element :f] : | hash_flow.rb:679:12:679:16 | [post] hash1 [element :f] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :f] : | hash_flow.rb:679:12:683:7 | call to update [element :f] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :f] : | hash_flow.rb:679:41:679:49 | old_value : | -| hash_flow.rb:679:25:679:29 | hash2 [element :f] : | hash_flow.rb:679:52:679:60 | new_value : | -| hash_flow.rb:679:41:679:49 | old_value : | hash_flow.rb:681:14:681:22 | old_value | -| hash_flow.rb:679:52:679:60 | new_value : | hash_flow.rb:682:14:682:22 | new_value | -| hash_flow.rb:684:11:684:14 | hash [element :a] : | hash_flow.rb:684:11:684:18 | ...[...] : | -| hash_flow.rb:684:11:684:18 | ...[...] : | hash_flow.rb:684:10:684:19 | ( ... ) | -| hash_flow.rb:686:11:686:14 | hash [element :c] : | hash_flow.rb:686:11:686:18 | ...[...] : | -| hash_flow.rb:686:11:686:18 | ...[...] : | hash_flow.rb:686:10:686:19 | ( ... ) | -| hash_flow.rb:687:11:687:14 | hash [element :d] : | hash_flow.rb:687:11:687:18 | ...[...] : | -| hash_flow.rb:687:11:687:18 | ...[...] : | hash_flow.rb:687:10:687:19 | ( ... ) | -| hash_flow.rb:689:11:689:14 | hash [element :f] : | hash_flow.rb:689:11:689:18 | ...[...] : | -| hash_flow.rb:689:11:689:18 | ...[...] : | hash_flow.rb:689:10:689:19 | ( ... ) | -| hash_flow.rb:691:11:691:15 | hash1 [element :a] : | hash_flow.rb:691:11:691:19 | ...[...] : | -| hash_flow.rb:691:11:691:19 | ...[...] : | hash_flow.rb:691:10:691:20 | ( ... ) | -| hash_flow.rb:693:11:693:15 | hash1 [element :c] : | hash_flow.rb:693:11:693:19 | ...[...] : | -| hash_flow.rb:693:11:693:19 | ...[...] : | hash_flow.rb:693:10:693:20 | ( ... ) | -| hash_flow.rb:694:11:694:15 | hash1 [element :d] : | hash_flow.rb:694:11:694:19 | ...[...] : | -| hash_flow.rb:694:11:694:19 | ...[...] : | hash_flow.rb:694:10:694:20 | ( ... ) | -| hash_flow.rb:696:11:696:15 | hash1 [element :f] : | hash_flow.rb:696:11:696:19 | ...[...] : | -| hash_flow.rb:696:11:696:19 | ...[...] : | hash_flow.rb:696:10:696:20 | ( ... ) | -| hash_flow.rb:703:15:703:25 | call to taint : | hash_flow.rb:707:9:707:12 | hash [element :a] : | -| hash_flow.rb:705:15:705:25 | call to taint : | hash_flow.rb:707:9:707:12 | hash [element :c] : | -| hash_flow.rb:707:9:707:12 | hash [element :a] : | hash_flow.rb:707:9:707:19 | call to values [element] : | -| hash_flow.rb:707:9:707:12 | hash [element :c] : | hash_flow.rb:707:9:707:19 | call to values [element] : | -| hash_flow.rb:707:9:707:19 | call to values [element] : | hash_flow.rb:708:11:708:11 | a [element] : | -| hash_flow.rb:708:11:708:11 | a [element] : | hash_flow.rb:708:11:708:14 | ...[...] : | -| hash_flow.rb:708:11:708:14 | ...[...] : | hash_flow.rb:708:10:708:15 | ( ... ) | -| hash_flow.rb:715:15:715:25 | call to taint : | hash_flow.rb:719:9:719:12 | hash [element :a] : | -| hash_flow.rb:715:15:715:25 | call to taint : | hash_flow.rb:721:9:721:12 | hash [element :a] : | -| hash_flow.rb:717:15:717:25 | call to taint : | hash_flow.rb:721:9:721:12 | hash [element :c] : | -| hash_flow.rb:719:9:719:12 | hash [element :a] : | hash_flow.rb:719:9:719:26 | call to values_at [element 0] : | -| hash_flow.rb:719:9:719:26 | call to values_at [element 0] : | hash_flow.rb:720:10:720:10 | b [element 0] : | -| hash_flow.rb:720:10:720:10 | b [element 0] : | hash_flow.rb:720:10:720:13 | ...[...] | -| hash_flow.rb:721:9:721:12 | hash [element :a] : | hash_flow.rb:721:9:721:31 | call to fetch_values [element] : | -| hash_flow.rb:721:9:721:12 | hash [element :c] : | hash_flow.rb:721:9:721:31 | call to fetch_values [element] : | -| hash_flow.rb:721:9:721:31 | call to fetch_values [element] : | hash_flow.rb:722:10:722:10 | b [element] : | -| hash_flow.rb:722:10:722:10 | b [element] : | hash_flow.rb:722:10:722:13 | ...[...] | -| hash_flow.rb:729:15:729:25 | call to taint : | hash_flow.rb:738:16:738:20 | hash1 [element :a] : | -| hash_flow.rb:731:15:731:25 | call to taint : | hash_flow.rb:738:16:738:20 | hash1 [element :c] : | -| hash_flow.rb:734:15:734:25 | call to taint : | hash_flow.rb:738:44:738:48 | hash2 [element :d] : | -| hash_flow.rb:736:15:736:25 | call to taint : | hash_flow.rb:738:44:738:48 | hash2 [element :f] : | -| hash_flow.rb:738:14:738:20 | ** ... [element :a] : | hash_flow.rb:739:10:739:13 | hash [element :a] : | -| hash_flow.rb:738:14:738:20 | ** ... [element :c] : | hash_flow.rb:741:10:741:13 | hash [element :c] : | -| hash_flow.rb:738:16:738:20 | hash1 [element :a] : | hash_flow.rb:738:14:738:20 | ** ... [element :a] : | -| hash_flow.rb:738:16:738:20 | hash1 [element :c] : | hash_flow.rb:738:14:738:20 | ** ... [element :c] : | -| hash_flow.rb:738:29:738:39 | call to taint : | hash_flow.rb:745:10:745:13 | hash [element :g] : | -| hash_flow.rb:738:42:738:48 | ** ... [element :d] : | hash_flow.rb:742:10:742:13 | hash [element :d] : | -| hash_flow.rb:738:42:738:48 | ** ... [element :f] : | hash_flow.rb:744:10:744:13 | hash [element :f] : | -| hash_flow.rb:738:44:738:48 | hash2 [element :d] : | hash_flow.rb:738:42:738:48 | ** ... [element :d] : | -| hash_flow.rb:738:44:738:48 | hash2 [element :f] : | hash_flow.rb:738:42:738:48 | ** ... [element :f] : | -| hash_flow.rb:739:10:739:13 | hash [element :a] : | hash_flow.rb:739:10:739:17 | ...[...] | -| hash_flow.rb:741:10:741:13 | hash [element :c] : | hash_flow.rb:741:10:741:17 | ...[...] | -| hash_flow.rb:742:10:742:13 | hash [element :d] : | hash_flow.rb:742:10:742:17 | ...[...] | -| hash_flow.rb:744:10:744:13 | hash [element :f] : | hash_flow.rb:744:10:744:17 | ...[...] | -| hash_flow.rb:745:10:745:13 | hash [element :g] : | hash_flow.rb:745:10:745:17 | ...[...] | +| hash_flow.rb:72:13:72:45 | ...[...] [element a] : | hash_flow.rb:73:10:73:14 | hash5 [element a] : | +| hash_flow.rb:72:18:72:34 | Pair [pair a] : | hash_flow.rb:72:13:72:45 | ...[...] [element a] : | +| hash_flow.rb:72:25:72:34 | call to taint : | hash_flow.rb:72:18:72:34 | Pair [pair a] : | +| hash_flow.rb:73:10:73:14 | hash5 [element a] : | hash_flow.rb:73:10:73:19 | ...[...] | +| hash_flow.rb:76:13:76:47 | ...[...] [element a] : | hash_flow.rb:77:10:77:14 | hash6 [element a] : | +| hash_flow.rb:76:19:76:35 | Pair [pair a] : | hash_flow.rb:76:13:76:47 | ...[...] [element a] : | +| hash_flow.rb:76:26:76:35 | call to taint : | hash_flow.rb:76:19:76:35 | Pair [pair a] : | +| hash_flow.rb:77:10:77:14 | hash6 [element a] : | hash_flow.rb:77:10:77:19 | ...[...] | +| hash_flow.rb:84:13:84:42 | call to [] [element :a] : | hash_flow.rb:85:10:85:14 | hash1 [element :a] : | +| hash_flow.rb:84:26:84:35 | call to taint : | hash_flow.rb:84:13:84:42 | call to [] [element :a] : | +| hash_flow.rb:85:10:85:14 | hash1 [element :a] : | hash_flow.rb:85:10:85:18 | ...[...] | +| hash_flow.rb:93:15:93:24 | call to taint : | hash_flow.rb:96:30:96:33 | hash [element :a] : | +| hash_flow.rb:96:13:96:34 | call to try_convert [element :a] : | hash_flow.rb:97:10:97:14 | hash2 [element :a] : | +| hash_flow.rb:96:30:96:33 | hash [element :a] : | hash_flow.rb:96:13:96:34 | call to try_convert [element :a] : | +| hash_flow.rb:97:10:97:14 | hash2 [element :a] : | hash_flow.rb:97:10:97:18 | ...[...] | +| hash_flow.rb:105:21:105:30 | call to taint : | hash_flow.rb:106:10:106:10 | b | +| hash_flow.rb:113:9:113:12 | [post] hash [element :a] : | hash_flow.rb:114:10:114:13 | hash [element :a] : | +| hash_flow.rb:113:9:113:34 | call to store : | hash_flow.rb:115:10:115:10 | b | +| hash_flow.rb:113:24:113:33 | call to taint : | hash_flow.rb:113:9:113:12 | [post] hash [element :a] : | +| hash_flow.rb:113:24:113:33 | call to taint : | hash_flow.rb:113:9:113:34 | call to store : | +| hash_flow.rb:114:10:114:13 | hash [element :a] : | hash_flow.rb:114:10:114:17 | ...[...] | +| hash_flow.rb:118:9:118:12 | [post] hash [element] : | hash_flow.rb:119:10:119:13 | hash [element] : | +| hash_flow.rb:118:9:118:12 | [post] hash [element] : | hash_flow.rb:120:10:120:13 | hash [element] : | +| hash_flow.rb:118:9:118:33 | call to store : | hash_flow.rb:121:10:121:10 | c | +| hash_flow.rb:118:23:118:32 | call to taint : | hash_flow.rb:118:9:118:12 | [post] hash [element] : | +| hash_flow.rb:118:23:118:32 | call to taint : | hash_flow.rb:118:9:118:33 | call to store : | +| hash_flow.rb:119:10:119:13 | hash [element] : | hash_flow.rb:119:10:119:17 | ...[...] | +| hash_flow.rb:120:10:120:13 | hash [element] : | hash_flow.rb:120:10:120:17 | ...[...] | +| hash_flow.rb:128:15:128:24 | call to taint : | hash_flow.rb:131:5:131:8 | hash [element :a] : | +| hash_flow.rb:128:15:128:24 | call to taint : | hash_flow.rb:134:5:134:8 | hash [element :a] : | +| hash_flow.rb:131:5:131:8 | hash [element :a] : | hash_flow.rb:131:18:131:29 | key_or_value : | +| hash_flow.rb:131:18:131:29 | key_or_value : | hash_flow.rb:132:14:132:25 | key_or_value | +| hash_flow.rb:134:5:134:8 | hash [element :a] : | hash_flow.rb:134:22:134:26 | value : | +| hash_flow.rb:134:22:134:26 | value : | hash_flow.rb:136:14:136:18 | value | +| hash_flow.rb:144:15:144:25 | call to taint : | hash_flow.rb:147:9:147:12 | hash [element :a] : | +| hash_flow.rb:144:15:144:25 | call to taint : | hash_flow.rb:151:9:151:12 | hash [element :a] : | +| hash_flow.rb:147:9:147:12 | hash [element :a] : | hash_flow.rb:147:9:147:22 | call to assoc [element 1] : | +| hash_flow.rb:147:9:147:22 | call to assoc [element 1] : | hash_flow.rb:149:10:149:10 | b [element 1] : | +| hash_flow.rb:147:9:147:22 | call to assoc [element 1] : | hash_flow.rb:150:10:150:10 | b [element 1] : | +| hash_flow.rb:149:10:149:10 | b [element 1] : | hash_flow.rb:149:10:149:13 | ...[...] | +| hash_flow.rb:150:10:150:10 | b [element 1] : | hash_flow.rb:150:10:150:13 | ...[...] | +| hash_flow.rb:151:9:151:12 | hash [element :a] : | hash_flow.rb:151:9:151:21 | call to assoc [element 1] : | +| hash_flow.rb:151:9:151:21 | call to assoc [element 1] : | hash_flow.rb:152:10:152:10 | c [element 1] : | +| hash_flow.rb:152:10:152:10 | c [element 1] : | hash_flow.rb:152:10:152:13 | ...[...] | +| hash_flow.rb:170:15:170:25 | call to taint : | hash_flow.rb:173:9:173:12 | hash [element :a] : | +| hash_flow.rb:173:9:173:12 | hash [element :a] : | hash_flow.rb:173:9:173:20 | call to compact [element :a] : | +| hash_flow.rb:173:9:173:20 | call to compact [element :a] : | hash_flow.rb:174:10:174:10 | a [element :a] : | +| hash_flow.rb:174:10:174:10 | a [element :a] : | hash_flow.rb:174:10:174:14 | ...[...] | +| hash_flow.rb:182:15:182:25 | call to taint : | hash_flow.rb:185:9:185:12 | hash [element :a] : | +| hash_flow.rb:185:9:185:12 | hash [element :a] : | hash_flow.rb:185:9:185:23 | call to delete : | +| hash_flow.rb:185:9:185:23 | call to delete : | hash_flow.rb:186:10:186:10 | a | +| hash_flow.rb:194:15:194:25 | call to taint : | hash_flow.rb:197:9:197:12 | hash [element :a] : | +| hash_flow.rb:194:15:194:25 | call to taint : | hash_flow.rb:202:10:202:13 | hash [element :a] : | +| hash_flow.rb:197:9:197:12 | [post] hash [element :a] : | hash_flow.rb:202:10:202:13 | hash [element :a] : | +| hash_flow.rb:197:9:197:12 | hash [element :a] : | hash_flow.rb:197:9:197:12 | [post] hash [element :a] : | +| hash_flow.rb:197:9:197:12 | hash [element :a] : | hash_flow.rb:197:9:200:7 | call to delete_if [element :a] : | +| hash_flow.rb:197:9:197:12 | hash [element :a] : | hash_flow.rb:197:33:197:37 | value : | +| hash_flow.rb:197:9:200:7 | call to delete_if [element :a] : | hash_flow.rb:201:10:201:10 | a [element :a] : | +| hash_flow.rb:197:33:197:37 | value : | hash_flow.rb:199:14:199:18 | value | +| hash_flow.rb:201:10:201:10 | a [element :a] : | hash_flow.rb:201:10:201:14 | ...[...] | +| hash_flow.rb:202:10:202:13 | hash [element :a] : | hash_flow.rb:202:10:202:17 | ...[...] | +| hash_flow.rb:210:15:210:25 | call to taint : | hash_flow.rb:217:10:217:13 | hash [element :a] : | +| hash_flow.rb:213:19:213:29 | call to taint : | hash_flow.rb:219:10:219:13 | hash [element :c, element :d] : | +| hash_flow.rb:217:10:217:13 | hash [element :a] : | hash_flow.rb:217:10:217:21 | call to dig | +| hash_flow.rb:219:10:219:13 | hash [element :c, element :d] : | hash_flow.rb:219:10:219:24 | call to dig | +| hash_flow.rb:227:15:227:25 | call to taint : | hash_flow.rb:230:9:230:12 | hash [element :a] : | +| hash_flow.rb:230:9:230:12 | hash [element :a] : | hash_flow.rb:230:9:233:7 | call to each [element :a] : | +| hash_flow.rb:230:9:230:12 | hash [element :a] : | hash_flow.rb:230:28:230:32 | value : | +| hash_flow.rb:230:9:233:7 | call to each [element :a] : | hash_flow.rb:234:10:234:10 | x [element :a] : | +| hash_flow.rb:230:28:230:32 | value : | hash_flow.rb:232:14:232:18 | value | +| hash_flow.rb:234:10:234:10 | x [element :a] : | hash_flow.rb:234:10:234:14 | ...[...] | +| hash_flow.rb:242:15:242:25 | call to taint : | hash_flow.rb:245:9:245:12 | hash [element :a] : | +| hash_flow.rb:245:9:245:12 | hash [element :a] : | hash_flow.rb:245:9:247:7 | call to each_key [element :a] : | +| hash_flow.rb:245:9:247:7 | call to each_key [element :a] : | hash_flow.rb:248:10:248:10 | x [element :a] : | +| hash_flow.rb:248:10:248:10 | x [element :a] : | hash_flow.rb:248:10:248:14 | ...[...] | +| hash_flow.rb:256:15:256:25 | call to taint : | hash_flow.rb:259:9:259:12 | hash [element :a] : | +| hash_flow.rb:259:9:259:12 | hash [element :a] : | hash_flow.rb:259:9:262:7 | call to each_pair [element :a] : | +| hash_flow.rb:259:9:259:12 | hash [element :a] : | hash_flow.rb:259:33:259:37 | value : | +| hash_flow.rb:259:9:262:7 | call to each_pair [element :a] : | hash_flow.rb:263:10:263:10 | x [element :a] : | +| hash_flow.rb:259:33:259:37 | value : | hash_flow.rb:261:14:261:18 | value | +| hash_flow.rb:263:10:263:10 | x [element :a] : | hash_flow.rb:263:10:263:14 | ...[...] | +| hash_flow.rb:271:15:271:25 | call to taint : | hash_flow.rb:274:9:274:12 | hash [element :a] : | +| hash_flow.rb:274:9:274:12 | hash [element :a] : | hash_flow.rb:274:9:276:7 | call to each_value [element :a] : | +| hash_flow.rb:274:9:274:12 | hash [element :a] : | hash_flow.rb:274:29:274:33 | value : | +| hash_flow.rb:274:9:276:7 | call to each_value [element :a] : | hash_flow.rb:277:10:277:10 | x [element :a] : | +| hash_flow.rb:274:29:274:33 | value : | hash_flow.rb:275:14:275:18 | value | +| hash_flow.rb:277:10:277:10 | x [element :a] : | hash_flow.rb:277:10:277:14 | ...[...] | +| hash_flow.rb:287:15:287:25 | call to taint : | hash_flow.rb:290:9:290:12 | hash [element :c] : | +| hash_flow.rb:290:9:290:12 | hash [element :c] : | hash_flow.rb:290:9:290:28 | call to except [element :c] : | +| hash_flow.rb:290:9:290:28 | call to except [element :c] : | hash_flow.rb:293:10:293:10 | x [element :c] : | +| hash_flow.rb:293:10:293:10 | x [element :c] : | hash_flow.rb:293:10:293:14 | ...[...] | +| hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:305:9:305:12 | hash [element :a] : | +| hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:309:9:309:12 | hash [element :a] : | +| hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:311:9:311:12 | hash [element :a] : | +| hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:315:9:315:12 | hash [element :a] : | +| hash_flow.rb:303:15:303:25 | call to taint : | hash_flow.rb:305:9:305:12 | hash [element :c] : | +| hash_flow.rb:303:15:303:25 | call to taint : | hash_flow.rb:315:9:315:12 | hash [element :c] : | +| hash_flow.rb:305:9:305:12 | hash [element :a] : | hash_flow.rb:305:9:307:7 | call to fetch : | +| hash_flow.rb:305:9:305:12 | hash [element :c] : | hash_flow.rb:305:9:307:7 | call to fetch : | +| hash_flow.rb:305:9:307:7 | call to fetch : | hash_flow.rb:308:10:308:10 | b | +| hash_flow.rb:305:20:305:30 | call to taint : | hash_flow.rb:305:37:305:37 | x : | +| hash_flow.rb:305:37:305:37 | x : | hash_flow.rb:306:14:306:14 | x | +| hash_flow.rb:309:9:309:12 | hash [element :a] : | hash_flow.rb:309:9:309:22 | call to fetch : | +| hash_flow.rb:309:9:309:22 | call to fetch : | hash_flow.rb:310:10:310:10 | b | +| hash_flow.rb:311:9:311:12 | hash [element :a] : | hash_flow.rb:311:9:311:35 | call to fetch : | +| hash_flow.rb:311:9:311:35 | call to fetch : | hash_flow.rb:312:10:312:10 | b | +| hash_flow.rb:311:24:311:34 | call to taint : | hash_flow.rb:311:9:311:35 | call to fetch : | +| hash_flow.rb:313:9:313:35 | call to fetch : | hash_flow.rb:314:10:314:10 | b | +| hash_flow.rb:313:24:313:34 | call to taint : | hash_flow.rb:313:9:313:35 | call to fetch : | +| hash_flow.rb:315:9:315:12 | hash [element :a] : | hash_flow.rb:315:9:315:34 | call to fetch : | +| hash_flow.rb:315:9:315:12 | hash [element :c] : | hash_flow.rb:315:9:315:34 | call to fetch : | +| hash_flow.rb:315:9:315:34 | call to fetch : | hash_flow.rb:316:10:316:10 | b | +| hash_flow.rb:315:23:315:33 | call to taint : | hash_flow.rb:315:9:315:34 | call to fetch : | +| hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:327:9:327:12 | hash [element :a] : | +| hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:332:9:332:12 | hash [element :a] : | +| hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:334:9:334:12 | hash [element :a] : | +| hash_flow.rb:325:15:325:25 | call to taint : | hash_flow.rb:327:9:327:12 | hash [element :c] : | +| hash_flow.rb:325:15:325:25 | call to taint : | hash_flow.rb:334:9:334:12 | hash [element :c] : | +| hash_flow.rb:327:9:327:12 | hash [element :a] : | hash_flow.rb:327:9:330:7 | call to fetch_values [element] : | +| hash_flow.rb:327:9:327:12 | hash [element :c] : | hash_flow.rb:327:9:330:7 | call to fetch_values [element] : | +| hash_flow.rb:327:9:330:7 | call to fetch_values [element] : | hash_flow.rb:331:10:331:10 | b [element] : | +| hash_flow.rb:327:27:327:37 | call to taint : | hash_flow.rb:327:44:327:44 | x : | +| hash_flow.rb:327:44:327:44 | x : | hash_flow.rb:328:14:328:14 | x | +| hash_flow.rb:329:9:329:19 | call to taint : | hash_flow.rb:327:9:330:7 | call to fetch_values [element] : | +| hash_flow.rb:331:10:331:10 | b [element] : | hash_flow.rb:331:10:331:13 | ...[...] | +| hash_flow.rb:332:9:332:12 | hash [element :a] : | hash_flow.rb:332:9:332:29 | call to fetch_values [element] : | +| hash_flow.rb:332:9:332:29 | call to fetch_values [element] : | hash_flow.rb:333:10:333:10 | b [element] : | +| hash_flow.rb:333:10:333:10 | b [element] : | hash_flow.rb:333:10:333:13 | ...[...] | +| hash_flow.rb:334:9:334:12 | hash [element :a] : | hash_flow.rb:334:9:334:31 | call to fetch_values [element] : | +| hash_flow.rb:334:9:334:12 | hash [element :c] : | hash_flow.rb:334:9:334:31 | call to fetch_values [element] : | +| hash_flow.rb:334:9:334:31 | call to fetch_values [element] : | hash_flow.rb:335:10:335:10 | b [element] : | +| hash_flow.rb:335:10:335:10 | b [element] : | hash_flow.rb:335:10:335:13 | ...[...] | +| hash_flow.rb:342:15:342:25 | call to taint : | hash_flow.rb:346:9:346:12 | hash [element :a] : | +| hash_flow.rb:344:15:344:25 | call to taint : | hash_flow.rb:346:9:346:12 | hash [element :c] : | +| hash_flow.rb:346:9:346:12 | hash [element :a] : | hash_flow.rb:346:9:350:7 | call to filter [element :a] : | +| hash_flow.rb:346:9:346:12 | hash [element :a] : | hash_flow.rb:346:30:346:34 | value : | +| hash_flow.rb:346:9:346:12 | hash [element :c] : | hash_flow.rb:346:30:346:34 | value : | +| hash_flow.rb:346:9:350:7 | call to filter [element :a] : | hash_flow.rb:351:11:351:11 | b [element :a] : | +| hash_flow.rb:346:30:346:34 | value : | hash_flow.rb:348:14:348:18 | value | +| hash_flow.rb:351:11:351:11 | b [element :a] : | hash_flow.rb:351:11:351:15 | ...[...] : | +| hash_flow.rb:351:11:351:15 | ...[...] : | hash_flow.rb:351:10:351:16 | ( ... ) | +| hash_flow.rb:358:15:358:25 | call to taint : | hash_flow.rb:362:5:362:8 | hash [element :a] : | +| hash_flow.rb:360:15:360:25 | call to taint : | hash_flow.rb:362:5:362:8 | hash [element :c] : | +| hash_flow.rb:362:5:362:8 | [post] hash [element :a] : | hash_flow.rb:367:11:367:14 | hash [element :a] : | +| hash_flow.rb:362:5:362:8 | hash [element :a] : | hash_flow.rb:362:5:362:8 | [post] hash [element :a] : | +| hash_flow.rb:362:5:362:8 | hash [element :a] : | hash_flow.rb:362:27:362:31 | value : | +| hash_flow.rb:362:5:362:8 | hash [element :c] : | hash_flow.rb:362:27:362:31 | value : | +| hash_flow.rb:362:27:362:31 | value : | hash_flow.rb:364:14:364:18 | value | +| hash_flow.rb:367:11:367:14 | hash [element :a] : | hash_flow.rb:367:11:367:18 | ...[...] : | +| hash_flow.rb:367:11:367:18 | ...[...] : | hash_flow.rb:367:10:367:19 | ( ... ) | +| hash_flow.rb:374:15:374:25 | call to taint : | hash_flow.rb:378:9:378:12 | hash [element :a] : | +| hash_flow.rb:376:15:376:25 | call to taint : | hash_flow.rb:378:9:378:12 | hash [element :c] : | +| hash_flow.rb:378:9:378:12 | hash [element :a] : | hash_flow.rb:378:9:378:20 | call to flatten [element] : | +| hash_flow.rb:378:9:378:12 | hash [element :c] : | hash_flow.rb:378:9:378:20 | call to flatten [element] : | +| hash_flow.rb:378:9:378:20 | call to flatten [element] : | hash_flow.rb:379:11:379:11 | b [element] : | +| hash_flow.rb:379:11:379:11 | b [element] : | hash_flow.rb:379:11:379:14 | ...[...] : | +| hash_flow.rb:379:11:379:14 | ...[...] : | hash_flow.rb:379:10:379:15 | ( ... ) | +| hash_flow.rb:386:15:386:25 | call to taint : | hash_flow.rb:390:9:390:12 | hash [element :a] : | +| hash_flow.rb:388:15:388:25 | call to taint : | hash_flow.rb:390:9:390:12 | hash [element :c] : | +| hash_flow.rb:390:9:390:12 | [post] hash [element :a] : | hash_flow.rb:395:11:395:14 | hash [element :a] : | +| hash_flow.rb:390:9:390:12 | hash [element :a] : | hash_flow.rb:390:9:390:12 | [post] hash [element :a] : | +| hash_flow.rb:390:9:390:12 | hash [element :a] : | hash_flow.rb:390:9:394:7 | call to keep_if [element :a] : | +| hash_flow.rb:390:9:390:12 | hash [element :a] : | hash_flow.rb:390:31:390:35 | value : | +| hash_flow.rb:390:9:390:12 | hash [element :c] : | hash_flow.rb:390:31:390:35 | value : | +| hash_flow.rb:390:9:394:7 | call to keep_if [element :a] : | hash_flow.rb:396:11:396:11 | b [element :a] : | +| hash_flow.rb:390:31:390:35 | value : | hash_flow.rb:392:14:392:18 | value | +| hash_flow.rb:395:11:395:14 | hash [element :a] : | hash_flow.rb:395:11:395:18 | ...[...] : | +| hash_flow.rb:395:11:395:18 | ...[...] : | hash_flow.rb:395:10:395:19 | ( ... ) | +| hash_flow.rb:396:11:396:11 | b [element :a] : | hash_flow.rb:396:11:396:15 | ...[...] : | +| hash_flow.rb:396:11:396:15 | ...[...] : | hash_flow.rb:396:10:396:16 | ( ... ) | +| hash_flow.rb:403:15:403:25 | call to taint : | hash_flow.rb:412:12:412:16 | hash1 [element :a] : | +| hash_flow.rb:405:15:405:25 | call to taint : | hash_flow.rb:412:12:412:16 | hash1 [element :c] : | +| hash_flow.rb:408:15:408:25 | call to taint : | hash_flow.rb:412:24:412:28 | hash2 [element :d] : | +| hash_flow.rb:410:15:410:25 | call to taint : | hash_flow.rb:412:24:412:28 | hash2 [element :f] : | +| hash_flow.rb:412:12:412:16 | hash1 [element :a] : | hash_flow.rb:412:12:416:7 | call to merge [element :a] : | +| hash_flow.rb:412:12:412:16 | hash1 [element :a] : | hash_flow.rb:412:40:412:48 | old_value : | +| hash_flow.rb:412:12:412:16 | hash1 [element :a] : | hash_flow.rb:412:51:412:59 | new_value : | +| hash_flow.rb:412:12:412:16 | hash1 [element :c] : | hash_flow.rb:412:12:416:7 | call to merge [element :c] : | +| hash_flow.rb:412:12:412:16 | hash1 [element :c] : | hash_flow.rb:412:40:412:48 | old_value : | +| hash_flow.rb:412:12:412:16 | hash1 [element :c] : | hash_flow.rb:412:51:412:59 | new_value : | +| hash_flow.rb:412:12:416:7 | call to merge [element :a] : | hash_flow.rb:417:11:417:14 | hash [element :a] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :c] : | hash_flow.rb:419:11:419:14 | hash [element :c] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :d] : | hash_flow.rb:420:11:420:14 | hash [element :d] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :f] : | hash_flow.rb:422:11:422:14 | hash [element :f] : | +| hash_flow.rb:412:24:412:28 | hash2 [element :d] : | hash_flow.rb:412:12:416:7 | call to merge [element :d] : | +| hash_flow.rb:412:24:412:28 | hash2 [element :d] : | hash_flow.rb:412:40:412:48 | old_value : | +| hash_flow.rb:412:24:412:28 | hash2 [element :d] : | hash_flow.rb:412:51:412:59 | new_value : | +| hash_flow.rb:412:24:412:28 | hash2 [element :f] : | hash_flow.rb:412:12:416:7 | call to merge [element :f] : | +| hash_flow.rb:412:24:412:28 | hash2 [element :f] : | hash_flow.rb:412:40:412:48 | old_value : | +| hash_flow.rb:412:24:412:28 | hash2 [element :f] : | hash_flow.rb:412:51:412:59 | new_value : | +| hash_flow.rb:412:40:412:48 | old_value : | hash_flow.rb:414:14:414:22 | old_value | +| hash_flow.rb:412:51:412:59 | new_value : | hash_flow.rb:415:14:415:22 | new_value | +| hash_flow.rb:417:11:417:14 | hash [element :a] : | hash_flow.rb:417:11:417:18 | ...[...] : | +| hash_flow.rb:417:11:417:18 | ...[...] : | hash_flow.rb:417:10:417:19 | ( ... ) | +| hash_flow.rb:419:11:419:14 | hash [element :c] : | hash_flow.rb:419:11:419:18 | ...[...] : | +| hash_flow.rb:419:11:419:18 | ...[...] : | hash_flow.rb:419:10:419:19 | ( ... ) | +| hash_flow.rb:420:11:420:14 | hash [element :d] : | hash_flow.rb:420:11:420:18 | ...[...] : | +| hash_flow.rb:420:11:420:18 | ...[...] : | hash_flow.rb:420:10:420:19 | ( ... ) | +| hash_flow.rb:422:11:422:14 | hash [element :f] : | hash_flow.rb:422:11:422:18 | ...[...] : | +| hash_flow.rb:422:11:422:18 | ...[...] : | hash_flow.rb:422:10:422:19 | ( ... ) | +| hash_flow.rb:429:15:429:25 | call to taint : | hash_flow.rb:438:12:438:16 | hash1 [element :a] : | +| hash_flow.rb:431:15:431:25 | call to taint : | hash_flow.rb:438:12:438:16 | hash1 [element :c] : | +| hash_flow.rb:434:15:434:25 | call to taint : | hash_flow.rb:438:25:438:29 | hash2 [element :d] : | +| hash_flow.rb:436:15:436:25 | call to taint : | hash_flow.rb:438:25:438:29 | hash2 [element :f] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :a] : | hash_flow.rb:450:11:450:15 | hash1 [element :a] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :c] : | hash_flow.rb:452:11:452:15 | hash1 [element :c] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :d] : | hash_flow.rb:453:11:453:15 | hash1 [element :d] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :f] : | hash_flow.rb:455:11:455:15 | hash1 [element :f] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :a] : | hash_flow.rb:438:12:438:16 | [post] hash1 [element :a] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :a] : | hash_flow.rb:438:12:442:7 | call to merge! [element :a] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :a] : | hash_flow.rb:438:41:438:49 | old_value : | +| hash_flow.rb:438:12:438:16 | hash1 [element :a] : | hash_flow.rb:438:52:438:60 | new_value : | +| hash_flow.rb:438:12:438:16 | hash1 [element :c] : | hash_flow.rb:438:12:438:16 | [post] hash1 [element :c] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :c] : | hash_flow.rb:438:12:442:7 | call to merge! [element :c] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :c] : | hash_flow.rb:438:41:438:49 | old_value : | +| hash_flow.rb:438:12:438:16 | hash1 [element :c] : | hash_flow.rb:438:52:438:60 | new_value : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :a] : | hash_flow.rb:443:11:443:14 | hash [element :a] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :c] : | hash_flow.rb:445:11:445:14 | hash [element :c] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :d] : | hash_flow.rb:446:11:446:14 | hash [element :d] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :f] : | hash_flow.rb:448:11:448:14 | hash [element :f] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :d] : | hash_flow.rb:438:12:438:16 | [post] hash1 [element :d] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :d] : | hash_flow.rb:438:12:442:7 | call to merge! [element :d] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :d] : | hash_flow.rb:438:41:438:49 | old_value : | +| hash_flow.rb:438:25:438:29 | hash2 [element :d] : | hash_flow.rb:438:52:438:60 | new_value : | +| hash_flow.rb:438:25:438:29 | hash2 [element :f] : | hash_flow.rb:438:12:438:16 | [post] hash1 [element :f] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :f] : | hash_flow.rb:438:12:442:7 | call to merge! [element :f] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :f] : | hash_flow.rb:438:41:438:49 | old_value : | +| hash_flow.rb:438:25:438:29 | hash2 [element :f] : | hash_flow.rb:438:52:438:60 | new_value : | +| hash_flow.rb:438:41:438:49 | old_value : | hash_flow.rb:440:14:440:22 | old_value | +| hash_flow.rb:438:52:438:60 | new_value : | hash_flow.rb:441:14:441:22 | new_value | +| hash_flow.rb:443:11:443:14 | hash [element :a] : | hash_flow.rb:443:11:443:18 | ...[...] : | +| hash_flow.rb:443:11:443:18 | ...[...] : | hash_flow.rb:443:10:443:19 | ( ... ) | +| hash_flow.rb:445:11:445:14 | hash [element :c] : | hash_flow.rb:445:11:445:18 | ...[...] : | +| hash_flow.rb:445:11:445:18 | ...[...] : | hash_flow.rb:445:10:445:19 | ( ... ) | +| hash_flow.rb:446:11:446:14 | hash [element :d] : | hash_flow.rb:446:11:446:18 | ...[...] : | +| hash_flow.rb:446:11:446:18 | ...[...] : | hash_flow.rb:446:10:446:19 | ( ... ) | +| hash_flow.rb:448:11:448:14 | hash [element :f] : | hash_flow.rb:448:11:448:18 | ...[...] : | +| hash_flow.rb:448:11:448:18 | ...[...] : | hash_flow.rb:448:10:448:19 | ( ... ) | +| hash_flow.rb:450:11:450:15 | hash1 [element :a] : | hash_flow.rb:450:11:450:19 | ...[...] : | +| hash_flow.rb:450:11:450:19 | ...[...] : | hash_flow.rb:450:10:450:20 | ( ... ) | +| hash_flow.rb:452:11:452:15 | hash1 [element :c] : | hash_flow.rb:452:11:452:19 | ...[...] : | +| hash_flow.rb:452:11:452:19 | ...[...] : | hash_flow.rb:452:10:452:20 | ( ... ) | +| hash_flow.rb:453:11:453:15 | hash1 [element :d] : | hash_flow.rb:453:11:453:19 | ...[...] : | +| hash_flow.rb:453:11:453:19 | ...[...] : | hash_flow.rb:453:10:453:20 | ( ... ) | +| hash_flow.rb:455:11:455:15 | hash1 [element :f] : | hash_flow.rb:455:11:455:19 | ...[...] : | +| hash_flow.rb:455:11:455:19 | ...[...] : | hash_flow.rb:455:10:455:20 | ( ... ) | +| hash_flow.rb:462:15:462:25 | call to taint : | hash_flow.rb:465:9:465:12 | hash [element :a] : | +| hash_flow.rb:465:9:465:12 | hash [element :a] : | hash_flow.rb:465:9:465:22 | call to rassoc [element 1] : | +| hash_flow.rb:465:9:465:22 | call to rassoc [element 1] : | hash_flow.rb:467:10:467:10 | b [element 1] : | +| hash_flow.rb:467:10:467:10 | b [element 1] : | hash_flow.rb:467:10:467:13 | ...[...] | +| hash_flow.rb:474:15:474:25 | call to taint : | hash_flow.rb:477:9:477:12 | hash [element :a] : | +| hash_flow.rb:477:9:477:12 | hash [element :a] : | hash_flow.rb:477:9:481:7 | call to reject [element :a] : | +| hash_flow.rb:477:9:477:12 | hash [element :a] : | hash_flow.rb:477:29:477:33 | value : | +| hash_flow.rb:477:9:481:7 | call to reject [element :a] : | hash_flow.rb:482:10:482:10 | b [element :a] : | +| hash_flow.rb:477:29:477:33 | value : | hash_flow.rb:479:14:479:18 | value | +| hash_flow.rb:482:10:482:10 | b [element :a] : | hash_flow.rb:482:10:482:14 | ...[...] | +| hash_flow.rb:489:15:489:25 | call to taint : | hash_flow.rb:492:9:492:12 | hash [element :a] : | +| hash_flow.rb:489:15:489:25 | call to taint : | hash_flow.rb:498:10:498:13 | hash [element :a] : | +| hash_flow.rb:492:9:492:12 | [post] hash [element :a] : | hash_flow.rb:498:10:498:13 | hash [element :a] : | +| hash_flow.rb:492:9:492:12 | hash [element :a] : | hash_flow.rb:492:9:492:12 | [post] hash [element :a] : | +| hash_flow.rb:492:9:492:12 | hash [element :a] : | hash_flow.rb:492:9:496:7 | call to reject! [element :a] : | +| hash_flow.rb:492:9:492:12 | hash [element :a] : | hash_flow.rb:492:30:492:34 | value : | +| hash_flow.rb:492:9:496:7 | call to reject! [element :a] : | hash_flow.rb:497:10:497:10 | b [element :a] : | +| hash_flow.rb:492:30:492:34 | value : | hash_flow.rb:494:14:494:18 | value | +| hash_flow.rb:497:10:497:10 | b [element :a] : | hash_flow.rb:497:10:497:14 | ...[...] | +| hash_flow.rb:498:10:498:13 | hash [element :a] : | hash_flow.rb:498:10:498:17 | ...[...] | +| hash_flow.rb:505:15:505:25 | call to taint : | hash_flow.rb:512:19:512:22 | hash [element :a] : | +| hash_flow.rb:507:15:507:25 | call to taint : | hash_flow.rb:512:19:512:22 | hash [element :c] : | +| hash_flow.rb:512:5:512:9 | [post] hash2 [element :a] : | hash_flow.rb:513:11:513:15 | hash2 [element :a] : | +| hash_flow.rb:512:5:512:9 | [post] hash2 [element :c] : | hash_flow.rb:515:11:515:15 | hash2 [element :c] : | +| hash_flow.rb:512:19:512:22 | hash [element :a] : | hash_flow.rb:512:5:512:9 | [post] hash2 [element :a] : | +| hash_flow.rb:512:19:512:22 | hash [element :c] : | hash_flow.rb:512:5:512:9 | [post] hash2 [element :c] : | +| hash_flow.rb:513:11:513:15 | hash2 [element :a] : | hash_flow.rb:513:11:513:19 | ...[...] : | +| hash_flow.rb:513:11:513:19 | ...[...] : | hash_flow.rb:513:10:513:20 | ( ... ) | +| hash_flow.rb:515:11:515:15 | hash2 [element :c] : | hash_flow.rb:515:11:515:19 | ...[...] : | +| hash_flow.rb:515:11:515:19 | ...[...] : | hash_flow.rb:515:10:515:20 | ( ... ) | +| hash_flow.rb:520:15:520:25 | call to taint : | hash_flow.rb:524:9:524:12 | hash [element :a] : | +| hash_flow.rb:522:15:522:25 | call to taint : | hash_flow.rb:524:9:524:12 | hash [element :c] : | +| hash_flow.rb:524:9:524:12 | hash [element :a] : | hash_flow.rb:524:9:528:7 | call to select [element :a] : | +| hash_flow.rb:524:9:524:12 | hash [element :a] : | hash_flow.rb:524:30:524:34 | value : | +| hash_flow.rb:524:9:524:12 | hash [element :c] : | hash_flow.rb:524:30:524:34 | value : | +| hash_flow.rb:524:9:528:7 | call to select [element :a] : | hash_flow.rb:529:11:529:11 | b [element :a] : | +| hash_flow.rb:524:30:524:34 | value : | hash_flow.rb:526:14:526:18 | value | +| hash_flow.rb:529:11:529:11 | b [element :a] : | hash_flow.rb:529:11:529:15 | ...[...] : | +| hash_flow.rb:529:11:529:15 | ...[...] : | hash_flow.rb:529:10:529:16 | ( ... ) | +| hash_flow.rb:536:15:536:25 | call to taint : | hash_flow.rb:540:5:540:8 | hash [element :a] : | +| hash_flow.rb:538:15:538:25 | call to taint : | hash_flow.rb:540:5:540:8 | hash [element :c] : | +| hash_flow.rb:540:5:540:8 | [post] hash [element :a] : | hash_flow.rb:545:11:545:14 | hash [element :a] : | +| hash_flow.rb:540:5:540:8 | hash [element :a] : | hash_flow.rb:540:5:540:8 | [post] hash [element :a] : | +| hash_flow.rb:540:5:540:8 | hash [element :a] : | hash_flow.rb:540:27:540:31 | value : | +| hash_flow.rb:540:5:540:8 | hash [element :c] : | hash_flow.rb:540:27:540:31 | value : | +| hash_flow.rb:540:27:540:31 | value : | hash_flow.rb:542:14:542:18 | value | +| hash_flow.rb:545:11:545:14 | hash [element :a] : | hash_flow.rb:545:11:545:18 | ...[...] : | +| hash_flow.rb:545:11:545:18 | ...[...] : | hash_flow.rb:545:10:545:19 | ( ... ) | +| hash_flow.rb:552:15:552:25 | call to taint : | hash_flow.rb:556:9:556:12 | hash [element :a] : | +| hash_flow.rb:554:15:554:25 | call to taint : | hash_flow.rb:556:9:556:12 | hash [element :c] : | +| hash_flow.rb:556:9:556:12 | [post] hash [element :a] : | hash_flow.rb:557:11:557:14 | hash [element :a] : | +| hash_flow.rb:556:9:556:12 | hash [element :a] : | hash_flow.rb:556:9:556:12 | [post] hash [element :a] : | +| hash_flow.rb:556:9:556:12 | hash [element :a] : | hash_flow.rb:556:9:556:18 | call to shift [element 1] : | +| hash_flow.rb:556:9:556:12 | hash [element :c] : | hash_flow.rb:556:9:556:18 | call to shift [element 1] : | +| hash_flow.rb:556:9:556:18 | call to shift [element 1] : | hash_flow.rb:559:11:559:11 | b [element 1] : | +| hash_flow.rb:557:11:557:14 | hash [element :a] : | hash_flow.rb:557:11:557:18 | ...[...] : | +| hash_flow.rb:557:11:557:18 | ...[...] : | hash_flow.rb:557:10:557:19 | ( ... ) | +| hash_flow.rb:559:11:559:11 | b [element 1] : | hash_flow.rb:559:11:559:14 | ...[...] : | +| hash_flow.rb:559:11:559:14 | ...[...] : | hash_flow.rb:559:10:559:15 | ( ... ) | +| hash_flow.rb:566:15:566:25 | call to taint : | hash_flow.rb:570:9:570:12 | hash [element :a] : | +| hash_flow.rb:566:15:566:25 | call to taint : | hash_flow.rb:575:9:575:12 | hash [element :a] : | +| hash_flow.rb:568:15:568:25 | call to taint : | hash_flow.rb:575:9:575:12 | hash [element :c] : | +| hash_flow.rb:570:9:570:12 | hash [element :a] : | hash_flow.rb:570:9:570:26 | call to slice [element :a] : | +| hash_flow.rb:570:9:570:26 | call to slice [element :a] : | hash_flow.rb:571:11:571:11 | b [element :a] : | +| hash_flow.rb:571:11:571:11 | b [element :a] : | hash_flow.rb:571:11:571:15 | ...[...] : | +| hash_flow.rb:571:11:571:15 | ...[...] : | hash_flow.rb:571:10:571:16 | ( ... ) | +| hash_flow.rb:575:9:575:12 | hash [element :a] : | hash_flow.rb:575:9:575:25 | call to slice [element :a] : | +| hash_flow.rb:575:9:575:12 | hash [element :c] : | hash_flow.rb:575:9:575:25 | call to slice [element :c] : | +| hash_flow.rb:575:9:575:25 | call to slice [element :a] : | hash_flow.rb:576:11:576:11 | c [element :a] : | +| hash_flow.rb:575:9:575:25 | call to slice [element :c] : | hash_flow.rb:578:11:578:11 | c [element :c] : | +| hash_flow.rb:576:11:576:11 | c [element :a] : | hash_flow.rb:576:11:576:15 | ...[...] : | +| hash_flow.rb:576:11:576:15 | ...[...] : | hash_flow.rb:576:10:576:16 | ( ... ) | +| hash_flow.rb:578:11:578:11 | c [element :c] : | hash_flow.rb:578:11:578:15 | ...[...] : | +| hash_flow.rb:578:11:578:15 | ...[...] : | hash_flow.rb:578:10:578:16 | ( ... ) | +| hash_flow.rb:585:15:585:25 | call to taint : | hash_flow.rb:589:9:589:12 | hash [element :a] : | +| hash_flow.rb:587:15:587:25 | call to taint : | hash_flow.rb:589:9:589:12 | hash [element :c] : | +| hash_flow.rb:589:9:589:12 | hash [element :a] : | hash_flow.rb:589:9:589:17 | call to to_a [element, element 1] : | +| hash_flow.rb:589:9:589:12 | hash [element :c] : | hash_flow.rb:589:9:589:17 | call to to_a [element, element 1] : | +| hash_flow.rb:589:9:589:17 | call to to_a [element, element 1] : | hash_flow.rb:591:11:591:11 | a [element, element 1] : | +| hash_flow.rb:591:11:591:11 | a [element, element 1] : | hash_flow.rb:591:11:591:14 | ...[...] [element 1] : | +| hash_flow.rb:591:11:591:14 | ...[...] [element 1] : | hash_flow.rb:591:11:591:17 | ...[...] : | +| hash_flow.rb:591:11:591:17 | ...[...] : | hash_flow.rb:591:10:591:18 | ( ... ) | +| hash_flow.rb:598:15:598:25 | call to taint : | hash_flow.rb:602:9:602:12 | hash [element :a] : | +| hash_flow.rb:598:15:598:25 | call to taint : | hash_flow.rb:607:9:607:12 | hash [element :a] : | +| hash_flow.rb:600:15:600:25 | call to taint : | hash_flow.rb:602:9:602:12 | hash [element :c] : | +| hash_flow.rb:600:15:600:25 | call to taint : | hash_flow.rb:607:9:607:12 | hash [element :c] : | +| hash_flow.rb:602:9:602:12 | hash [element :a] : | hash_flow.rb:602:9:602:17 | call to to_h [element :a] : | +| hash_flow.rb:602:9:602:12 | hash [element :c] : | hash_flow.rb:602:9:602:17 | call to to_h [element :c] : | +| hash_flow.rb:602:9:602:17 | call to to_h [element :a] : | hash_flow.rb:603:11:603:11 | a [element :a] : | +| hash_flow.rb:602:9:602:17 | call to to_h [element :c] : | hash_flow.rb:605:11:605:11 | a [element :c] : | +| hash_flow.rb:603:11:603:11 | a [element :a] : | hash_flow.rb:603:11:603:15 | ...[...] : | +| hash_flow.rb:603:11:603:15 | ...[...] : | hash_flow.rb:603:10:603:16 | ( ... ) | +| hash_flow.rb:605:11:605:11 | a [element :c] : | hash_flow.rb:605:11:605:15 | ...[...] : | +| hash_flow.rb:605:11:605:15 | ...[...] : | hash_flow.rb:605:10:605:16 | ( ... ) | +| hash_flow.rb:607:9:607:12 | hash [element :a] : | hash_flow.rb:607:28:607:32 | value : | +| hash_flow.rb:607:9:607:12 | hash [element :c] : | hash_flow.rb:607:28:607:32 | value : | +| hash_flow.rb:607:9:611:7 | call to to_h [element] : | hash_flow.rb:612:11:612:11 | b [element] : | +| hash_flow.rb:607:28:607:32 | value : | hash_flow.rb:609:14:609:18 | value | +| hash_flow.rb:610:14:610:24 | call to taint : | hash_flow.rb:607:9:611:7 | call to to_h [element] : | +| hash_flow.rb:612:11:612:11 | b [element] : | hash_flow.rb:612:11:612:15 | ...[...] : | +| hash_flow.rb:612:11:612:15 | ...[...] : | hash_flow.rb:612:10:612:16 | ( ... ) | +| hash_flow.rb:619:15:619:25 | call to taint : | hash_flow.rb:623:9:623:12 | hash [element :a] : | +| hash_flow.rb:621:15:621:25 | call to taint : | hash_flow.rb:623:9:623:12 | hash [element :c] : | +| hash_flow.rb:623:9:623:12 | hash [element :a] : | hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | +| hash_flow.rb:623:9:623:12 | hash [element :c] : | hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | +| hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | hash_flow.rb:624:11:624:11 | a [element] : | +| hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | hash_flow.rb:625:11:625:11 | a [element] : | +| hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | hash_flow.rb:626:11:626:11 | a [element] : | +| hash_flow.rb:624:11:624:11 | a [element] : | hash_flow.rb:624:11:624:16 | ...[...] : | +| hash_flow.rb:624:11:624:16 | ...[...] : | hash_flow.rb:624:10:624:17 | ( ... ) | +| hash_flow.rb:625:11:625:11 | a [element] : | hash_flow.rb:625:11:625:16 | ...[...] : | +| hash_flow.rb:625:11:625:16 | ...[...] : | hash_flow.rb:625:10:625:17 | ( ... ) | +| hash_flow.rb:626:11:626:11 | a [element] : | hash_flow.rb:626:11:626:16 | ...[...] : | +| hash_flow.rb:626:11:626:16 | ...[...] : | hash_flow.rb:626:10:626:17 | ( ... ) | +| hash_flow.rb:633:15:633:25 | call to taint : | hash_flow.rb:637:5:637:8 | hash [element :a] : | +| hash_flow.rb:635:15:635:25 | call to taint : | hash_flow.rb:637:5:637:8 | hash [element :c] : | +| hash_flow.rb:637:5:637:8 | [post] hash [element] : | hash_flow.rb:638:11:638:14 | hash [element] : | +| hash_flow.rb:637:5:637:8 | [post] hash [element] : | hash_flow.rb:639:11:639:14 | hash [element] : | +| hash_flow.rb:637:5:637:8 | [post] hash [element] : | hash_flow.rb:640:11:640:14 | hash [element] : | +| hash_flow.rb:637:5:637:8 | hash [element :a] : | hash_flow.rb:637:5:637:8 | [post] hash [element] : | +| hash_flow.rb:637:5:637:8 | hash [element :c] : | hash_flow.rb:637:5:637:8 | [post] hash [element] : | +| hash_flow.rb:638:11:638:14 | hash [element] : | hash_flow.rb:638:11:638:19 | ...[...] : | +| hash_flow.rb:638:11:638:19 | ...[...] : | hash_flow.rb:638:10:638:20 | ( ... ) | +| hash_flow.rb:639:11:639:14 | hash [element] : | hash_flow.rb:639:11:639:19 | ...[...] : | +| hash_flow.rb:639:11:639:19 | ...[...] : | hash_flow.rb:639:10:639:20 | ( ... ) | +| hash_flow.rb:640:11:640:14 | hash [element] : | hash_flow.rb:640:11:640:19 | ...[...] : | +| hash_flow.rb:640:11:640:19 | ...[...] : | hash_flow.rb:640:10:640:20 | ( ... ) | +| hash_flow.rb:647:15:647:25 | call to taint : | hash_flow.rb:651:9:651:12 | hash [element :a] : | +| hash_flow.rb:647:15:647:25 | call to taint : | hash_flow.rb:655:11:655:14 | hash [element :a] : | +| hash_flow.rb:649:15:649:25 | call to taint : | hash_flow.rb:651:9:651:12 | hash [element :c] : | +| hash_flow.rb:651:9:651:12 | hash [element :a] : | hash_flow.rb:651:35:651:39 | value : | +| hash_flow.rb:651:9:651:12 | hash [element :c] : | hash_flow.rb:651:35:651:39 | value : | +| hash_flow.rb:651:9:654:7 | call to transform_values [element] : | hash_flow.rb:656:11:656:11 | b [element] : | +| hash_flow.rb:651:35:651:39 | value : | hash_flow.rb:652:14:652:18 | value | +| hash_flow.rb:653:9:653:19 | call to taint : | hash_flow.rb:651:9:654:7 | call to transform_values [element] : | +| hash_flow.rb:655:11:655:14 | hash [element :a] : | hash_flow.rb:655:11:655:18 | ...[...] : | +| hash_flow.rb:655:11:655:18 | ...[...] : | hash_flow.rb:655:10:655:19 | ( ... ) | +| hash_flow.rb:656:11:656:11 | b [element] : | hash_flow.rb:656:11:656:15 | ...[...] : | +| hash_flow.rb:656:11:656:15 | ...[...] : | hash_flow.rb:656:10:656:16 | ( ... ) | +| hash_flow.rb:663:15:663:25 | call to taint : | hash_flow.rb:667:5:667:8 | hash [element :a] : | +| hash_flow.rb:665:15:665:25 | call to taint : | hash_flow.rb:667:5:667:8 | hash [element :c] : | +| hash_flow.rb:667:5:667:8 | [post] hash [element] : | hash_flow.rb:671:11:671:14 | hash [element] : | +| hash_flow.rb:667:5:667:8 | hash [element :a] : | hash_flow.rb:667:32:667:36 | value : | +| hash_flow.rb:667:5:667:8 | hash [element :c] : | hash_flow.rb:667:32:667:36 | value : | +| hash_flow.rb:667:32:667:36 | value : | hash_flow.rb:668:14:668:18 | value | +| hash_flow.rb:669:9:669:19 | call to taint : | hash_flow.rb:667:5:667:8 | [post] hash [element] : | +| hash_flow.rb:671:11:671:14 | hash [element] : | hash_flow.rb:671:11:671:18 | ...[...] : | +| hash_flow.rb:671:11:671:18 | ...[...] : | hash_flow.rb:671:10:671:19 | ( ... ) | +| hash_flow.rb:678:15:678:25 | call to taint : | hash_flow.rb:687:12:687:16 | hash1 [element :a] : | +| hash_flow.rb:680:15:680:25 | call to taint : | hash_flow.rb:687:12:687:16 | hash1 [element :c] : | +| hash_flow.rb:683:15:683:25 | call to taint : | hash_flow.rb:687:25:687:29 | hash2 [element :d] : | +| hash_flow.rb:685:15:685:25 | call to taint : | hash_flow.rb:687:25:687:29 | hash2 [element :f] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :a] : | hash_flow.rb:699:11:699:15 | hash1 [element :a] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :c] : | hash_flow.rb:701:11:701:15 | hash1 [element :c] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :d] : | hash_flow.rb:702:11:702:15 | hash1 [element :d] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :f] : | hash_flow.rb:704:11:704:15 | hash1 [element :f] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :a] : | hash_flow.rb:687:12:687:16 | [post] hash1 [element :a] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :a] : | hash_flow.rb:687:12:691:7 | call to update [element :a] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :a] : | hash_flow.rb:687:41:687:49 | old_value : | +| hash_flow.rb:687:12:687:16 | hash1 [element :a] : | hash_flow.rb:687:52:687:60 | new_value : | +| hash_flow.rb:687:12:687:16 | hash1 [element :c] : | hash_flow.rb:687:12:687:16 | [post] hash1 [element :c] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :c] : | hash_flow.rb:687:12:691:7 | call to update [element :c] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :c] : | hash_flow.rb:687:41:687:49 | old_value : | +| hash_flow.rb:687:12:687:16 | hash1 [element :c] : | hash_flow.rb:687:52:687:60 | new_value : | +| hash_flow.rb:687:12:691:7 | call to update [element :a] : | hash_flow.rb:692:11:692:14 | hash [element :a] : | +| hash_flow.rb:687:12:691:7 | call to update [element :c] : | hash_flow.rb:694:11:694:14 | hash [element :c] : | +| hash_flow.rb:687:12:691:7 | call to update [element :d] : | hash_flow.rb:695:11:695:14 | hash [element :d] : | +| hash_flow.rb:687:12:691:7 | call to update [element :f] : | hash_flow.rb:697:11:697:14 | hash [element :f] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :d] : | hash_flow.rb:687:12:687:16 | [post] hash1 [element :d] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :d] : | hash_flow.rb:687:12:691:7 | call to update [element :d] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :d] : | hash_flow.rb:687:41:687:49 | old_value : | +| hash_flow.rb:687:25:687:29 | hash2 [element :d] : | hash_flow.rb:687:52:687:60 | new_value : | +| hash_flow.rb:687:25:687:29 | hash2 [element :f] : | hash_flow.rb:687:12:687:16 | [post] hash1 [element :f] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :f] : | hash_flow.rb:687:12:691:7 | call to update [element :f] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :f] : | hash_flow.rb:687:41:687:49 | old_value : | +| hash_flow.rb:687:25:687:29 | hash2 [element :f] : | hash_flow.rb:687:52:687:60 | new_value : | +| hash_flow.rb:687:41:687:49 | old_value : | hash_flow.rb:689:14:689:22 | old_value | +| hash_flow.rb:687:52:687:60 | new_value : | hash_flow.rb:690:14:690:22 | new_value | +| hash_flow.rb:692:11:692:14 | hash [element :a] : | hash_flow.rb:692:11:692:18 | ...[...] : | +| hash_flow.rb:692:11:692:18 | ...[...] : | hash_flow.rb:692:10:692:19 | ( ... ) | +| hash_flow.rb:694:11:694:14 | hash [element :c] : | hash_flow.rb:694:11:694:18 | ...[...] : | +| hash_flow.rb:694:11:694:18 | ...[...] : | hash_flow.rb:694:10:694:19 | ( ... ) | +| hash_flow.rb:695:11:695:14 | hash [element :d] : | hash_flow.rb:695:11:695:18 | ...[...] : | +| hash_flow.rb:695:11:695:18 | ...[...] : | hash_flow.rb:695:10:695:19 | ( ... ) | +| hash_flow.rb:697:11:697:14 | hash [element :f] : | hash_flow.rb:697:11:697:18 | ...[...] : | +| hash_flow.rb:697:11:697:18 | ...[...] : | hash_flow.rb:697:10:697:19 | ( ... ) | +| hash_flow.rb:699:11:699:15 | hash1 [element :a] : | hash_flow.rb:699:11:699:19 | ...[...] : | +| hash_flow.rb:699:11:699:19 | ...[...] : | hash_flow.rb:699:10:699:20 | ( ... ) | +| hash_flow.rb:701:11:701:15 | hash1 [element :c] : | hash_flow.rb:701:11:701:19 | ...[...] : | +| hash_flow.rb:701:11:701:19 | ...[...] : | hash_flow.rb:701:10:701:20 | ( ... ) | +| hash_flow.rb:702:11:702:15 | hash1 [element :d] : | hash_flow.rb:702:11:702:19 | ...[...] : | +| hash_flow.rb:702:11:702:19 | ...[...] : | hash_flow.rb:702:10:702:20 | ( ... ) | +| hash_flow.rb:704:11:704:15 | hash1 [element :f] : | hash_flow.rb:704:11:704:19 | ...[...] : | +| hash_flow.rb:704:11:704:19 | ...[...] : | hash_flow.rb:704:10:704:20 | ( ... ) | +| hash_flow.rb:711:15:711:25 | call to taint : | hash_flow.rb:715:9:715:12 | hash [element :a] : | +| hash_flow.rb:713:15:713:25 | call to taint : | hash_flow.rb:715:9:715:12 | hash [element :c] : | +| hash_flow.rb:715:9:715:12 | hash [element :a] : | hash_flow.rb:715:9:715:19 | call to values [element] : | +| hash_flow.rb:715:9:715:12 | hash [element :c] : | hash_flow.rb:715:9:715:19 | call to values [element] : | +| hash_flow.rb:715:9:715:19 | call to values [element] : | hash_flow.rb:716:11:716:11 | a [element] : | +| hash_flow.rb:716:11:716:11 | a [element] : | hash_flow.rb:716:11:716:14 | ...[...] : | +| hash_flow.rb:716:11:716:14 | ...[...] : | hash_flow.rb:716:10:716:15 | ( ... ) | +| hash_flow.rb:723:15:723:25 | call to taint : | hash_flow.rb:727:9:727:12 | hash [element :a] : | +| hash_flow.rb:723:15:723:25 | call to taint : | hash_flow.rb:729:9:729:12 | hash [element :a] : | +| hash_flow.rb:725:15:725:25 | call to taint : | hash_flow.rb:729:9:729:12 | hash [element :c] : | +| hash_flow.rb:727:9:727:12 | hash [element :a] : | hash_flow.rb:727:9:727:26 | call to values_at [element 0] : | +| hash_flow.rb:727:9:727:26 | call to values_at [element 0] : | hash_flow.rb:728:10:728:10 | b [element 0] : | +| hash_flow.rb:728:10:728:10 | b [element 0] : | hash_flow.rb:728:10:728:13 | ...[...] | +| hash_flow.rb:729:9:729:12 | hash [element :a] : | hash_flow.rb:729:9:729:31 | call to fetch_values [element] : | +| hash_flow.rb:729:9:729:12 | hash [element :c] : | hash_flow.rb:729:9:729:31 | call to fetch_values [element] : | +| hash_flow.rb:729:9:729:31 | call to fetch_values [element] : | hash_flow.rb:730:10:730:10 | b [element] : | +| hash_flow.rb:730:10:730:10 | b [element] : | hash_flow.rb:730:10:730:13 | ...[...] | +| hash_flow.rb:737:15:737:25 | call to taint : | hash_flow.rb:746:16:746:20 | hash1 [element :a] : | +| hash_flow.rb:739:15:739:25 | call to taint : | hash_flow.rb:746:16:746:20 | hash1 [element :c] : | +| hash_flow.rb:742:15:742:25 | call to taint : | hash_flow.rb:746:44:746:48 | hash2 [element :d] : | +| hash_flow.rb:744:15:744:25 | call to taint : | hash_flow.rb:746:44:746:48 | hash2 [element :f] : | +| hash_flow.rb:746:14:746:20 | ** ... [element :a] : | hash_flow.rb:747:10:747:13 | hash [element :a] : | +| hash_flow.rb:746:14:746:20 | ** ... [element :c] : | hash_flow.rb:749:10:749:13 | hash [element :c] : | +| hash_flow.rb:746:16:746:20 | hash1 [element :a] : | hash_flow.rb:746:14:746:20 | ** ... [element :a] : | +| hash_flow.rb:746:16:746:20 | hash1 [element :c] : | hash_flow.rb:746:14:746:20 | ** ... [element :c] : | +| hash_flow.rb:746:29:746:39 | call to taint : | hash_flow.rb:753:10:753:13 | hash [element :g] : | +| hash_flow.rb:746:42:746:48 | ** ... [element :d] : | hash_flow.rb:750:10:750:13 | hash [element :d] : | +| hash_flow.rb:746:42:746:48 | ** ... [element :f] : | hash_flow.rb:752:10:752:13 | hash [element :f] : | +| hash_flow.rb:746:44:746:48 | hash2 [element :d] : | hash_flow.rb:746:42:746:48 | ** ... [element :d] : | +| hash_flow.rb:746:44:746:48 | hash2 [element :f] : | hash_flow.rb:746:42:746:48 | ** ... [element :f] : | +| hash_flow.rb:747:10:747:13 | hash [element :a] : | hash_flow.rb:747:10:747:17 | ...[...] | +| hash_flow.rb:749:10:749:13 | hash [element :c] : | hash_flow.rb:749:10:749:17 | ...[...] | +| hash_flow.rb:750:10:750:13 | hash [element :d] : | hash_flow.rb:750:10:750:17 | ...[...] | +| hash_flow.rb:752:10:752:13 | hash [element :f] : | hash_flow.rb:752:10:752:17 | ...[...] | +| hash_flow.rb:753:10:753:13 | hash [element :g] : | hash_flow.rb:753:10:753:17 | ...[...] | nodes | hash_flow.rb:11:15:11:24 | call to taint : | semmle.label | call to taint : | | hash_flow.rb:13:12:13:21 | call to taint : | semmle.label | call to taint : | @@ -590,515 +598,525 @@ nodes | hash_flow.rb:68:22:68:31 | call to taint : | semmle.label | call to taint : | | hash_flow.rb:69:10:69:14 | hash4 [element :a] : | semmle.label | hash4 [element :a] : | | hash_flow.rb:69:10:69:18 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:76:13:76:42 | call to [] [element :a] : | semmle.label | call to [] [element :a] : | +| hash_flow.rb:72:13:72:45 | ...[...] [element a] : | semmle.label | ...[...] [element a] : | +| hash_flow.rb:72:18:72:34 | Pair [pair a] : | semmle.label | Pair [pair a] : | +| hash_flow.rb:72:25:72:34 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:73:10:73:14 | hash5 [element a] : | semmle.label | hash5 [element a] : | +| hash_flow.rb:73:10:73:19 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:76:13:76:47 | ...[...] [element a] : | semmle.label | ...[...] [element a] : | +| hash_flow.rb:76:19:76:35 | Pair [pair a] : | semmle.label | Pair [pair a] : | | hash_flow.rb:76:26:76:35 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:77:10:77:14 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:77:10:77:18 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:85:15:85:24 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:88:13:88:34 | call to try_convert [element :a] : | semmle.label | call to try_convert [element :a] : | -| hash_flow.rb:88:30:88:33 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:89:10:89:14 | hash2 [element :a] : | semmle.label | hash2 [element :a] : | -| hash_flow.rb:89:10:89:18 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:97:21:97:30 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:98:10:98:10 | b | semmle.label | b | -| hash_flow.rb:105:9:105:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:105:9:105:34 | call to store : | semmle.label | call to store : | -| hash_flow.rb:105:24:105:33 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:106:10:106:13 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:106:10:106:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:107:10:107:10 | b | semmle.label | b | -| hash_flow.rb:110:9:110:12 | [post] hash [element] : | semmle.label | [post] hash [element] : | -| hash_flow.rb:110:9:110:33 | call to store : | semmle.label | call to store : | -| hash_flow.rb:110:23:110:32 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:111:10:111:13 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:111:10:111:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:112:10:112:13 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:112:10:112:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:113:10:113:10 | c | semmle.label | c | -| hash_flow.rb:120:15:120:24 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:123:5:123:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:123:18:123:29 | key_or_value : | semmle.label | key_or_value : | -| hash_flow.rb:124:14:124:25 | key_or_value | semmle.label | key_or_value | -| hash_flow.rb:126:5:126:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:126:22:126:26 | value : | semmle.label | value : | -| hash_flow.rb:128:14:128:18 | value | semmle.label | value | -| hash_flow.rb:136:15:136:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:139:9:139:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:139:9:139:22 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : | -| hash_flow.rb:141:10:141:10 | b [element 1] : | semmle.label | b [element 1] : | -| hash_flow.rb:141:10:141:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:142:10:142:10 | b [element 1] : | semmle.label | b [element 1] : | -| hash_flow.rb:142:10:142:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:143:9:143:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:143:9:143:21 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : | -| hash_flow.rb:144:10:144:10 | c [element 1] : | semmle.label | c [element 1] : | -| hash_flow.rb:144:10:144:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:162:15:162:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:165:9:165:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:165:9:165:20 | call to compact [element :a] : | semmle.label | call to compact [element :a] : | -| hash_flow.rb:166:10:166:10 | a [element :a] : | semmle.label | a [element :a] : | -| hash_flow.rb:166:10:166:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:174:15:174:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:177:9:177:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:177:9:177:23 | call to delete : | semmle.label | call to delete : | -| hash_flow.rb:178:10:178:10 | a | semmle.label | a | -| hash_flow.rb:186:15:186:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:189:9:189:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:189:9:189:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:189:9:192:7 | call to delete_if [element :a] : | semmle.label | call to delete_if [element :a] : | -| hash_flow.rb:189:33:189:37 | value : | semmle.label | value : | -| hash_flow.rb:191:14:191:18 | value | semmle.label | value | -| hash_flow.rb:193:10:193:10 | a [element :a] : | semmle.label | a [element :a] : | -| hash_flow.rb:193:10:193:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:194:10:194:13 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:194:10:194:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:202:15:202:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:205:19:205:29 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:209:10:209:13 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:209:10:209:21 | call to dig | semmle.label | call to dig | -| hash_flow.rb:211:10:211:13 | hash [element :c, element :d] : | semmle.label | hash [element :c, element :d] : | -| hash_flow.rb:211:10:211:24 | call to dig | semmle.label | call to dig | -| hash_flow.rb:219:15:219:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:222:9:222:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:222:9:225:7 | call to each [element :a] : | semmle.label | call to each [element :a] : | -| hash_flow.rb:222:28:222:32 | value : | semmle.label | value : | -| hash_flow.rb:224:14:224:18 | value | semmle.label | value | -| hash_flow.rb:226:10:226:10 | x [element :a] : | semmle.label | x [element :a] : | -| hash_flow.rb:226:10:226:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:234:15:234:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:237:9:237:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:237:9:239:7 | call to each_key [element :a] : | semmle.label | call to each_key [element :a] : | -| hash_flow.rb:240:10:240:10 | x [element :a] : | semmle.label | x [element :a] : | -| hash_flow.rb:240:10:240:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:248:15:248:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:251:9:251:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:251:9:254:7 | call to each_pair [element :a] : | semmle.label | call to each_pair [element :a] : | -| hash_flow.rb:251:33:251:37 | value : | semmle.label | value : | -| hash_flow.rb:253:14:253:18 | value | semmle.label | value | -| hash_flow.rb:255:10:255:10 | x [element :a] : | semmle.label | x [element :a] : | -| hash_flow.rb:255:10:255:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:263:15:263:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:266:9:266:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:266:9:268:7 | call to each_value [element :a] : | semmle.label | call to each_value [element :a] : | -| hash_flow.rb:266:29:266:33 | value : | semmle.label | value : | -| hash_flow.rb:267:14:267:18 | value | semmle.label | value | -| hash_flow.rb:269:10:269:10 | x [element :a] : | semmle.label | x [element :a] : | -| hash_flow.rb:269:10:269:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:279:15:279:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:282:9:282:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:282:9:282:28 | call to except [element :c] : | semmle.label | call to except [element :c] : | -| hash_flow.rb:285:10:285:10 | x [element :c] : | semmle.label | x [element :c] : | -| hash_flow.rb:285:10:285:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:293:15:293:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:295:15:295:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:297:9:297:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:297:9:297:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:297:9:299:7 | call to fetch : | semmle.label | call to fetch : | -| hash_flow.rb:297:20:297:30 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:297:37:297:37 | x : | semmle.label | x : | -| hash_flow.rb:298:14:298:14 | x | semmle.label | x | -| hash_flow.rb:300:10:300:10 | b | semmle.label | b | -| hash_flow.rb:301:9:301:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:301:9:301:22 | call to fetch : | semmle.label | call to fetch : | -| hash_flow.rb:302:10:302:10 | b | semmle.label | b | -| hash_flow.rb:303:9:303:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:303:9:303:35 | call to fetch : | semmle.label | call to fetch : | -| hash_flow.rb:303:24:303:34 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:304:10:304:10 | b | semmle.label | b | -| hash_flow.rb:305:9:305:35 | call to fetch : | semmle.label | call to fetch : | -| hash_flow.rb:305:24:305:34 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:306:10:306:10 | b | semmle.label | b | -| hash_flow.rb:307:9:307:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:307:9:307:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:307:9:307:34 | call to fetch : | semmle.label | call to fetch : | -| hash_flow.rb:307:23:307:33 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:77:10:77:14 | hash6 [element a] : | semmle.label | hash6 [element a] : | +| hash_flow.rb:77:10:77:19 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:84:13:84:42 | call to [] [element :a] : | semmle.label | call to [] [element :a] : | +| hash_flow.rb:84:26:84:35 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:85:10:85:14 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:85:10:85:18 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:93:15:93:24 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:96:13:96:34 | call to try_convert [element :a] : | semmle.label | call to try_convert [element :a] : | +| hash_flow.rb:96:30:96:33 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:97:10:97:14 | hash2 [element :a] : | semmle.label | hash2 [element :a] : | +| hash_flow.rb:97:10:97:18 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:105:21:105:30 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:106:10:106:10 | b | semmle.label | b | +| hash_flow.rb:113:9:113:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:113:9:113:34 | call to store : | semmle.label | call to store : | +| hash_flow.rb:113:24:113:33 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:114:10:114:13 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:114:10:114:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:115:10:115:10 | b | semmle.label | b | +| hash_flow.rb:118:9:118:12 | [post] hash [element] : | semmle.label | [post] hash [element] : | +| hash_flow.rb:118:9:118:33 | call to store : | semmle.label | call to store : | +| hash_flow.rb:118:23:118:32 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:119:10:119:13 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:119:10:119:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:120:10:120:13 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:120:10:120:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:121:10:121:10 | c | semmle.label | c | +| hash_flow.rb:128:15:128:24 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:131:5:131:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:131:18:131:29 | key_or_value : | semmle.label | key_or_value : | +| hash_flow.rb:132:14:132:25 | key_or_value | semmle.label | key_or_value | +| hash_flow.rb:134:5:134:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:134:22:134:26 | value : | semmle.label | value : | +| hash_flow.rb:136:14:136:18 | value | semmle.label | value | +| hash_flow.rb:144:15:144:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:147:9:147:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:147:9:147:22 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : | +| hash_flow.rb:149:10:149:10 | b [element 1] : | semmle.label | b [element 1] : | +| hash_flow.rb:149:10:149:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:150:10:150:10 | b [element 1] : | semmle.label | b [element 1] : | +| hash_flow.rb:150:10:150:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:151:9:151:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:151:9:151:21 | call to assoc [element 1] : | semmle.label | call to assoc [element 1] : | +| hash_flow.rb:152:10:152:10 | c [element 1] : | semmle.label | c [element 1] : | +| hash_flow.rb:152:10:152:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:170:15:170:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:173:9:173:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:173:9:173:20 | call to compact [element :a] : | semmle.label | call to compact [element :a] : | +| hash_flow.rb:174:10:174:10 | a [element :a] : | semmle.label | a [element :a] : | +| hash_flow.rb:174:10:174:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:182:15:182:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:185:9:185:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:185:9:185:23 | call to delete : | semmle.label | call to delete : | +| hash_flow.rb:186:10:186:10 | a | semmle.label | a | +| hash_flow.rb:194:15:194:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:197:9:197:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:197:9:197:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:197:9:200:7 | call to delete_if [element :a] : | semmle.label | call to delete_if [element :a] : | +| hash_flow.rb:197:33:197:37 | value : | semmle.label | value : | +| hash_flow.rb:199:14:199:18 | value | semmle.label | value | +| hash_flow.rb:201:10:201:10 | a [element :a] : | semmle.label | a [element :a] : | +| hash_flow.rb:201:10:201:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:202:10:202:13 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:202:10:202:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:210:15:210:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:213:19:213:29 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:217:10:217:13 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:217:10:217:21 | call to dig | semmle.label | call to dig | +| hash_flow.rb:219:10:219:13 | hash [element :c, element :d] : | semmle.label | hash [element :c, element :d] : | +| hash_flow.rb:219:10:219:24 | call to dig | semmle.label | call to dig | +| hash_flow.rb:227:15:227:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:230:9:230:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:230:9:233:7 | call to each [element :a] : | semmle.label | call to each [element :a] : | +| hash_flow.rb:230:28:230:32 | value : | semmle.label | value : | +| hash_flow.rb:232:14:232:18 | value | semmle.label | value | +| hash_flow.rb:234:10:234:10 | x [element :a] : | semmle.label | x [element :a] : | +| hash_flow.rb:234:10:234:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:242:15:242:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:245:9:245:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:245:9:247:7 | call to each_key [element :a] : | semmle.label | call to each_key [element :a] : | +| hash_flow.rb:248:10:248:10 | x [element :a] : | semmle.label | x [element :a] : | +| hash_flow.rb:248:10:248:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:256:15:256:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:259:9:259:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:259:9:262:7 | call to each_pair [element :a] : | semmle.label | call to each_pair [element :a] : | +| hash_flow.rb:259:33:259:37 | value : | semmle.label | value : | +| hash_flow.rb:261:14:261:18 | value | semmle.label | value | +| hash_flow.rb:263:10:263:10 | x [element :a] : | semmle.label | x [element :a] : | +| hash_flow.rb:263:10:263:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:271:15:271:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:274:9:274:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:274:9:276:7 | call to each_value [element :a] : | semmle.label | call to each_value [element :a] : | +| hash_flow.rb:274:29:274:33 | value : | semmle.label | value : | +| hash_flow.rb:275:14:275:18 | value | semmle.label | value | +| hash_flow.rb:277:10:277:10 | x [element :a] : | semmle.label | x [element :a] : | +| hash_flow.rb:277:10:277:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:287:15:287:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:290:9:290:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:290:9:290:28 | call to except [element :c] : | semmle.label | call to except [element :c] : | +| hash_flow.rb:293:10:293:10 | x [element :c] : | semmle.label | x [element :c] : | +| hash_flow.rb:293:10:293:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:301:15:301:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:303:15:303:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:305:9:305:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:305:9:305:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:305:9:307:7 | call to fetch : | semmle.label | call to fetch : | +| hash_flow.rb:305:20:305:30 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:305:37:305:37 | x : | semmle.label | x : | +| hash_flow.rb:306:14:306:14 | x | semmle.label | x | | hash_flow.rb:308:10:308:10 | b | semmle.label | b | -| hash_flow.rb:315:15:315:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:317:15:317:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:319:9:319:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:319:9:319:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:319:9:322:7 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | -| hash_flow.rb:319:27:319:37 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:319:44:319:44 | x : | semmle.label | x : | -| hash_flow.rb:320:14:320:14 | x | semmle.label | x | -| hash_flow.rb:321:9:321:19 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:323:10:323:10 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:323:10:323:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:324:9:324:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:324:9:324:29 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | -| hash_flow.rb:325:10:325:10 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:325:10:325:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:326:9:326:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:326:9:326:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:326:9:326:31 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | -| hash_flow.rb:327:10:327:10 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:327:10:327:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:334:15:334:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:336:15:336:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:338:9:338:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:338:9:338:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:338:9:342:7 | call to filter [element :a] : | semmle.label | call to filter [element :a] : | -| hash_flow.rb:338:30:338:34 | value : | semmle.label | value : | -| hash_flow.rb:340:14:340:18 | value | semmle.label | value | -| hash_flow.rb:343:10:343:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:343:11:343:11 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:343:11:343:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:350:15:350:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:352:15:352:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:354:5:354:8 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:354:5:354:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:354:5:354:8 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:354:27:354:31 | value : | semmle.label | value : | -| hash_flow.rb:356:14:356:18 | value | semmle.label | value | -| hash_flow.rb:359:10:359:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:359:11:359:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:359:11:359:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:366:15:366:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:368:15:368:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:370:9:370:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:370:9:370:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:370:9:370:20 | call to flatten [element] : | semmle.label | call to flatten [element] : | -| hash_flow.rb:371:10:371:15 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:371:11:371:11 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:371:11:371:14 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:378:15:378:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:380:15:380:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:382:9:382:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:382:9:382:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:382:9:382:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:382:9:386:7 | call to keep_if [element :a] : | semmle.label | call to keep_if [element :a] : | -| hash_flow.rb:382:31:382:35 | value : | semmle.label | value : | -| hash_flow.rb:384:14:384:18 | value | semmle.label | value | -| hash_flow.rb:387:10:387:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:387:11:387:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:387:11:387:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:388:10:388:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:388:11:388:11 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:388:11:388:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:395:15:395:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:397:15:397:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:400:15:400:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:402:15:402:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:404:12:404:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:404:12:404:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :a] : | semmle.label | call to merge [element :a] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :c] : | semmle.label | call to merge [element :c] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :d] : | semmle.label | call to merge [element :d] : | -| hash_flow.rb:404:12:408:7 | call to merge [element :f] : | semmle.label | call to merge [element :f] : | -| hash_flow.rb:404:24:404:28 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | -| hash_flow.rb:404:24:404:28 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | -| hash_flow.rb:404:40:404:48 | old_value : | semmle.label | old_value : | -| hash_flow.rb:404:51:404:59 | new_value : | semmle.label | new_value : | -| hash_flow.rb:406:14:406:22 | old_value | semmle.label | old_value | -| hash_flow.rb:407:14:407:22 | new_value | semmle.label | new_value | -| hash_flow.rb:409:10:409:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:409:11:409:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:409:11:409:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:411:10:411:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:411:11:411:14 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:411:11:411:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:412:10:412:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:412:11:412:14 | hash [element :d] : | semmle.label | hash [element :d] : | -| hash_flow.rb:412:11:412:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:414:10:414:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:414:11:414:14 | hash [element :f] : | semmle.label | hash [element :f] : | -| hash_flow.rb:414:11:414:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:421:15:421:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:423:15:423:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:426:15:426:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:428:15:428:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :a] : | semmle.label | [post] hash1 [element :a] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :c] : | semmle.label | [post] hash1 [element :c] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :d] : | semmle.label | [post] hash1 [element :d] : | -| hash_flow.rb:430:12:430:16 | [post] hash1 [element :f] : | semmle.label | [post] hash1 [element :f] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:430:12:430:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :a] : | semmle.label | call to merge! [element :a] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :c] : | semmle.label | call to merge! [element :c] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :d] : | semmle.label | call to merge! [element :d] : | -| hash_flow.rb:430:12:434:7 | call to merge! [element :f] : | semmle.label | call to merge! [element :f] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | -| hash_flow.rb:430:25:430:29 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | -| hash_flow.rb:430:41:430:49 | old_value : | semmle.label | old_value : | -| hash_flow.rb:430:52:430:60 | new_value : | semmle.label | new_value : | -| hash_flow.rb:432:14:432:22 | old_value | semmle.label | old_value | -| hash_flow.rb:433:14:433:22 | new_value | semmle.label | new_value | -| hash_flow.rb:435:10:435:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:435:11:435:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:435:11:435:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:437:10:437:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:437:11:437:14 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:437:11:437:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:438:10:438:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:438:11:438:14 | hash [element :d] : | semmle.label | hash [element :d] : | -| hash_flow.rb:438:11:438:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:440:10:440:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:440:11:440:14 | hash [element :f] : | semmle.label | hash [element :f] : | -| hash_flow.rb:440:11:440:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:442:10:442:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:442:11:442:15 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:442:11:442:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:444:10:444:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:444:11:444:15 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:444:11:444:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:445:10:445:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:445:11:445:15 | hash1 [element :d] : | semmle.label | hash1 [element :d] : | -| hash_flow.rb:445:11:445:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:447:10:447:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:447:11:447:15 | hash1 [element :f] : | semmle.label | hash1 [element :f] : | -| hash_flow.rb:447:11:447:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:454:15:454:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:457:9:457:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:457:9:457:22 | call to rassoc [element 1] : | semmle.label | call to rassoc [element 1] : | -| hash_flow.rb:459:10:459:10 | b [element 1] : | semmle.label | b [element 1] : | -| hash_flow.rb:459:10:459:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:466:15:466:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:469:9:469:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:469:9:473:7 | call to reject [element :a] : | semmle.label | call to reject [element :a] : | -| hash_flow.rb:469:29:469:33 | value : | semmle.label | value : | -| hash_flow.rb:471:14:471:18 | value | semmle.label | value | -| hash_flow.rb:474:10:474:10 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:474:10:474:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:481:15:481:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:484:9:484:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:484:9:484:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:484:9:488:7 | call to reject! [element :a] : | semmle.label | call to reject! [element :a] : | -| hash_flow.rb:484:30:484:34 | value : | semmle.label | value : | -| hash_flow.rb:486:14:486:18 | value | semmle.label | value | -| hash_flow.rb:489:10:489:10 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:489:10:489:14 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:490:10:490:13 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:490:10:490:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:497:15:497:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:499:15:499:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:504:5:504:9 | [post] hash2 [element :a] : | semmle.label | [post] hash2 [element :a] : | -| hash_flow.rb:504:5:504:9 | [post] hash2 [element :c] : | semmle.label | [post] hash2 [element :c] : | -| hash_flow.rb:504:19:504:22 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:504:19:504:22 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:505:10:505:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:505:11:505:15 | hash2 [element :a] : | semmle.label | hash2 [element :a] : | -| hash_flow.rb:505:11:505:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:507:10:507:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:507:11:507:15 | hash2 [element :c] : | semmle.label | hash2 [element :c] : | -| hash_flow.rb:507:11:507:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:512:15:512:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:514:15:514:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:516:9:516:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:516:9:516:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:516:9:520:7 | call to select [element :a] : | semmle.label | call to select [element :a] : | -| hash_flow.rb:516:30:516:34 | value : | semmle.label | value : | -| hash_flow.rb:518:14:518:18 | value | semmle.label | value | -| hash_flow.rb:521:10:521:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:521:11:521:11 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:521:11:521:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:528:15:528:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:530:15:530:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:532:5:532:8 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:532:5:532:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:532:5:532:8 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:532:27:532:31 | value : | semmle.label | value : | -| hash_flow.rb:534:14:534:18 | value | semmle.label | value | -| hash_flow.rb:537:10:537:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:537:11:537:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:537:11:537:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:544:15:544:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:546:15:546:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:548:9:548:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | -| hash_flow.rb:548:9:548:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:548:9:548:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:548:9:548:18 | call to shift [element 1] : | semmle.label | call to shift [element 1] : | -| hash_flow.rb:549:10:549:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:549:11:549:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:549:11:549:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:551:10:551:15 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:551:11:551:11 | b [element 1] : | semmle.label | b [element 1] : | -| hash_flow.rb:551:11:551:14 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:558:15:558:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:560:15:560:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:562:9:562:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:562:9:562:26 | call to slice [element :a] : | semmle.label | call to slice [element :a] : | -| hash_flow.rb:563:10:563:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:563:11:563:11 | b [element :a] : | semmle.label | b [element :a] : | -| hash_flow.rb:563:11:563:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:567:9:567:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:567:9:567:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:567:9:567:25 | call to slice [element :a] : | semmle.label | call to slice [element :a] : | -| hash_flow.rb:567:9:567:25 | call to slice [element :c] : | semmle.label | call to slice [element :c] : | -| hash_flow.rb:568:10:568:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:568:11:568:11 | c [element :a] : | semmle.label | c [element :a] : | -| hash_flow.rb:568:11:568:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:570:10:570:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:570:11:570:11 | c [element :c] : | semmle.label | c [element :c] : | -| hash_flow.rb:570:11:570:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:577:15:577:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:579:15:579:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:581:9:581:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:581:9:581:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:581:9:581:17 | call to to_a [element, element 1] : | semmle.label | call to to_a [element, element 1] : | -| hash_flow.rb:583:10:583:18 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:583:11:583:11 | a [element, element 1] : | semmle.label | a [element, element 1] : | -| hash_flow.rb:583:11:583:14 | ...[...] [element 1] : | semmle.label | ...[...] [element 1] : | -| hash_flow.rb:583:11:583:17 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:590:15:590:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:592:15:592:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:594:9:594:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:594:9:594:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:594:9:594:17 | call to to_h [element :a] : | semmle.label | call to to_h [element :a] : | -| hash_flow.rb:594:9:594:17 | call to to_h [element :c] : | semmle.label | call to to_h [element :c] : | -| hash_flow.rb:595:10:595:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:595:11:595:11 | a [element :a] : | semmle.label | a [element :a] : | -| hash_flow.rb:595:11:595:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:597:10:597:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:597:11:597:11 | a [element :c] : | semmle.label | a [element :c] : | -| hash_flow.rb:597:11:597:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:599:9:599:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:599:9:599:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:599:9:603:7 | call to to_h [element] : | semmle.label | call to to_h [element] : | -| hash_flow.rb:599:28:599:32 | value : | semmle.label | value : | -| hash_flow.rb:601:14:601:18 | value | semmle.label | value | -| hash_flow.rb:602:14:602:24 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:604:10:604:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:604:11:604:11 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:604:11:604:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:611:15:611:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:613:15:613:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:615:9:615:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:615:9:615:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:615:9:615:45 | call to transform_keys [element] : | semmle.label | call to transform_keys [element] : | -| hash_flow.rb:616:10:616:17 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:616:11:616:11 | a [element] : | semmle.label | a [element] : | -| hash_flow.rb:616:11:616:16 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:617:10:617:17 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:617:11:617:11 | a [element] : | semmle.label | a [element] : | -| hash_flow.rb:617:11:617:16 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:618:10:618:17 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:618:11:618:11 | a [element] : | semmle.label | a [element] : | -| hash_flow.rb:618:11:618:16 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:625:15:625:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:627:15:627:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:629:5:629:8 | [post] hash [element] : | semmle.label | [post] hash [element] : | -| hash_flow.rb:629:5:629:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:629:5:629:8 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:630:10:630:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:630:11:630:14 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:630:11:630:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:631:10:631:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:631:11:631:14 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:631:11:631:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:632:10:632:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:632:11:632:14 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:632:11:632:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:639:15:639:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:641:15:641:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:643:9:643:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:643:9:643:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:643:9:646:7 | call to transform_values [element] : | semmle.label | call to transform_values [element] : | -| hash_flow.rb:643:35:643:39 | value : | semmle.label | value : | -| hash_flow.rb:644:14:644:18 | value | semmle.label | value | -| hash_flow.rb:645:9:645:19 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:647:10:647:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:647:11:647:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:647:11:647:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:648:10:648:16 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:648:11:648:11 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:648:11:648:15 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:655:15:655:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:657:15:657:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:659:5:659:8 | [post] hash [element] : | semmle.label | [post] hash [element] : | -| hash_flow.rb:659:5:659:8 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:659:5:659:8 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:659:32:659:36 | value : | semmle.label | value : | -| hash_flow.rb:660:14:660:18 | value | semmle.label | value | -| hash_flow.rb:661:9:661:19 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:663:10:663:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:663:11:663:14 | hash [element] : | semmle.label | hash [element] : | -| hash_flow.rb:663:11:663:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:670:15:670:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:672:15:672:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:675:15:675:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:677:15:677:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :a] : | semmle.label | [post] hash1 [element :a] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :c] : | semmle.label | [post] hash1 [element :c] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :d] : | semmle.label | [post] hash1 [element :d] : | -| hash_flow.rb:679:12:679:16 | [post] hash1 [element :f] : | semmle.label | [post] hash1 [element :f] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:679:12:679:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:679:12:683:7 | call to update [element :a] : | semmle.label | call to update [element :a] : | -| hash_flow.rb:679:12:683:7 | call to update [element :c] : | semmle.label | call to update [element :c] : | -| hash_flow.rb:679:12:683:7 | call to update [element :d] : | semmle.label | call to update [element :d] : | -| hash_flow.rb:679:12:683:7 | call to update [element :f] : | semmle.label | call to update [element :f] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | -| hash_flow.rb:679:25:679:29 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | -| hash_flow.rb:679:41:679:49 | old_value : | semmle.label | old_value : | -| hash_flow.rb:679:52:679:60 | new_value : | semmle.label | new_value : | -| hash_flow.rb:681:14:681:22 | old_value | semmle.label | old_value | -| hash_flow.rb:682:14:682:22 | new_value | semmle.label | new_value | -| hash_flow.rb:684:10:684:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:684:11:684:14 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:684:11:684:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:686:10:686:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:686:11:686:14 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:686:11:686:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:687:10:687:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:687:11:687:14 | hash [element :d] : | semmle.label | hash [element :d] : | -| hash_flow.rb:687:11:687:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:689:10:689:19 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:689:11:689:14 | hash [element :f] : | semmle.label | hash [element :f] : | -| hash_flow.rb:689:11:689:18 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:691:10:691:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:691:11:691:15 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:691:11:691:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:693:10:693:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:693:11:693:15 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:693:11:693:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:694:10:694:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:694:11:694:15 | hash1 [element :d] : | semmle.label | hash1 [element :d] : | -| hash_flow.rb:694:11:694:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:696:10:696:20 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:696:11:696:15 | hash1 [element :f] : | semmle.label | hash1 [element :f] : | -| hash_flow.rb:696:11:696:19 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:703:15:703:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:705:15:705:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:707:9:707:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:707:9:707:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:707:9:707:19 | call to values [element] : | semmle.label | call to values [element] : | -| hash_flow.rb:708:10:708:15 | ( ... ) | semmle.label | ( ... ) | -| hash_flow.rb:708:11:708:11 | a [element] : | semmle.label | a [element] : | -| hash_flow.rb:708:11:708:14 | ...[...] : | semmle.label | ...[...] : | -| hash_flow.rb:715:15:715:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:717:15:717:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:719:9:719:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:719:9:719:26 | call to values_at [element 0] : | semmle.label | call to values_at [element 0] : | -| hash_flow.rb:720:10:720:10 | b [element 0] : | semmle.label | b [element 0] : | -| hash_flow.rb:720:10:720:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:721:9:721:12 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:721:9:721:12 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:721:9:721:31 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | -| hash_flow.rb:722:10:722:10 | b [element] : | semmle.label | b [element] : | -| hash_flow.rb:722:10:722:13 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:729:15:729:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:731:15:731:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:734:15:734:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:736:15:736:25 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:738:14:738:20 | ** ... [element :a] : | semmle.label | ** ... [element :a] : | -| hash_flow.rb:738:14:738:20 | ** ... [element :c] : | semmle.label | ** ... [element :c] : | -| hash_flow.rb:738:16:738:20 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | -| hash_flow.rb:738:16:738:20 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | -| hash_flow.rb:738:29:738:39 | call to taint : | semmle.label | call to taint : | -| hash_flow.rb:738:42:738:48 | ** ... [element :d] : | semmle.label | ** ... [element :d] : | -| hash_flow.rb:738:42:738:48 | ** ... [element :f] : | semmle.label | ** ... [element :f] : | -| hash_flow.rb:738:44:738:48 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | -| hash_flow.rb:738:44:738:48 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | -| hash_flow.rb:739:10:739:13 | hash [element :a] : | semmle.label | hash [element :a] : | -| hash_flow.rb:739:10:739:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:741:10:741:13 | hash [element :c] : | semmle.label | hash [element :c] : | -| hash_flow.rb:741:10:741:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:742:10:742:13 | hash [element :d] : | semmle.label | hash [element :d] : | -| hash_flow.rb:742:10:742:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:744:10:744:13 | hash [element :f] : | semmle.label | hash [element :f] : | -| hash_flow.rb:744:10:744:17 | ...[...] | semmle.label | ...[...] | -| hash_flow.rb:745:10:745:13 | hash [element :g] : | semmle.label | hash [element :g] : | -| hash_flow.rb:745:10:745:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:309:9:309:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:309:9:309:22 | call to fetch : | semmle.label | call to fetch : | +| hash_flow.rb:310:10:310:10 | b | semmle.label | b | +| hash_flow.rb:311:9:311:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:311:9:311:35 | call to fetch : | semmle.label | call to fetch : | +| hash_flow.rb:311:24:311:34 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:312:10:312:10 | b | semmle.label | b | +| hash_flow.rb:313:9:313:35 | call to fetch : | semmle.label | call to fetch : | +| hash_flow.rb:313:24:313:34 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:314:10:314:10 | b | semmle.label | b | +| hash_flow.rb:315:9:315:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:315:9:315:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:315:9:315:34 | call to fetch : | semmle.label | call to fetch : | +| hash_flow.rb:315:23:315:33 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:316:10:316:10 | b | semmle.label | b | +| hash_flow.rb:323:15:323:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:325:15:325:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:327:9:327:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:327:9:327:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:327:9:330:7 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | +| hash_flow.rb:327:27:327:37 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:327:44:327:44 | x : | semmle.label | x : | +| hash_flow.rb:328:14:328:14 | x | semmle.label | x | +| hash_flow.rb:329:9:329:19 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:331:10:331:10 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:331:10:331:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:332:9:332:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:332:9:332:29 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | +| hash_flow.rb:333:10:333:10 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:333:10:333:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:334:9:334:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:334:9:334:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:334:9:334:31 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | +| hash_flow.rb:335:10:335:10 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:335:10:335:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:342:15:342:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:344:15:344:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:346:9:346:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:346:9:346:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:346:9:350:7 | call to filter [element :a] : | semmle.label | call to filter [element :a] : | +| hash_flow.rb:346:30:346:34 | value : | semmle.label | value : | +| hash_flow.rb:348:14:348:18 | value | semmle.label | value | +| hash_flow.rb:351:10:351:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:351:11:351:11 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:351:11:351:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:358:15:358:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:360:15:360:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:362:5:362:8 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:362:5:362:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:362:5:362:8 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:362:27:362:31 | value : | semmle.label | value : | +| hash_flow.rb:364:14:364:18 | value | semmle.label | value | +| hash_flow.rb:367:10:367:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:367:11:367:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:367:11:367:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:374:15:374:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:376:15:376:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:378:9:378:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:378:9:378:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:378:9:378:20 | call to flatten [element] : | semmle.label | call to flatten [element] : | +| hash_flow.rb:379:10:379:15 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:379:11:379:11 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:379:11:379:14 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:386:15:386:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:388:15:388:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:390:9:390:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:390:9:390:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:390:9:390:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:390:9:394:7 | call to keep_if [element :a] : | semmle.label | call to keep_if [element :a] : | +| hash_flow.rb:390:31:390:35 | value : | semmle.label | value : | +| hash_flow.rb:392:14:392:18 | value | semmle.label | value | +| hash_flow.rb:395:10:395:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:395:11:395:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:395:11:395:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:396:10:396:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:396:11:396:11 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:396:11:396:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:403:15:403:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:405:15:405:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:408:15:408:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:410:15:410:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:412:12:412:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:412:12:412:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :a] : | semmle.label | call to merge [element :a] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :c] : | semmle.label | call to merge [element :c] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :d] : | semmle.label | call to merge [element :d] : | +| hash_flow.rb:412:12:416:7 | call to merge [element :f] : | semmle.label | call to merge [element :f] : | +| hash_flow.rb:412:24:412:28 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | +| hash_flow.rb:412:24:412:28 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | +| hash_flow.rb:412:40:412:48 | old_value : | semmle.label | old_value : | +| hash_flow.rb:412:51:412:59 | new_value : | semmle.label | new_value : | +| hash_flow.rb:414:14:414:22 | old_value | semmle.label | old_value | +| hash_flow.rb:415:14:415:22 | new_value | semmle.label | new_value | +| hash_flow.rb:417:10:417:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:417:11:417:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:417:11:417:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:419:10:419:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:419:11:419:14 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:419:11:419:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:420:10:420:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:420:11:420:14 | hash [element :d] : | semmle.label | hash [element :d] : | +| hash_flow.rb:420:11:420:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:422:10:422:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:422:11:422:14 | hash [element :f] : | semmle.label | hash [element :f] : | +| hash_flow.rb:422:11:422:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:429:15:429:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:431:15:431:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:434:15:434:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:436:15:436:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :a] : | semmle.label | [post] hash1 [element :a] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :c] : | semmle.label | [post] hash1 [element :c] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :d] : | semmle.label | [post] hash1 [element :d] : | +| hash_flow.rb:438:12:438:16 | [post] hash1 [element :f] : | semmle.label | [post] hash1 [element :f] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:438:12:438:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :a] : | semmle.label | call to merge! [element :a] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :c] : | semmle.label | call to merge! [element :c] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :d] : | semmle.label | call to merge! [element :d] : | +| hash_flow.rb:438:12:442:7 | call to merge! [element :f] : | semmle.label | call to merge! [element :f] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | +| hash_flow.rb:438:25:438:29 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | +| hash_flow.rb:438:41:438:49 | old_value : | semmle.label | old_value : | +| hash_flow.rb:438:52:438:60 | new_value : | semmle.label | new_value : | +| hash_flow.rb:440:14:440:22 | old_value | semmle.label | old_value | +| hash_flow.rb:441:14:441:22 | new_value | semmle.label | new_value | +| hash_flow.rb:443:10:443:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:443:11:443:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:443:11:443:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:445:10:445:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:445:11:445:14 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:445:11:445:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:446:10:446:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:446:11:446:14 | hash [element :d] : | semmle.label | hash [element :d] : | +| hash_flow.rb:446:11:446:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:448:10:448:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:448:11:448:14 | hash [element :f] : | semmle.label | hash [element :f] : | +| hash_flow.rb:448:11:448:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:450:10:450:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:450:11:450:15 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:450:11:450:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:452:10:452:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:452:11:452:15 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:452:11:452:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:453:10:453:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:453:11:453:15 | hash1 [element :d] : | semmle.label | hash1 [element :d] : | +| hash_flow.rb:453:11:453:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:455:10:455:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:455:11:455:15 | hash1 [element :f] : | semmle.label | hash1 [element :f] : | +| hash_flow.rb:455:11:455:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:462:15:462:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:465:9:465:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:465:9:465:22 | call to rassoc [element 1] : | semmle.label | call to rassoc [element 1] : | +| hash_flow.rb:467:10:467:10 | b [element 1] : | semmle.label | b [element 1] : | +| hash_flow.rb:467:10:467:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:474:15:474:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:477:9:477:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:477:9:481:7 | call to reject [element :a] : | semmle.label | call to reject [element :a] : | +| hash_flow.rb:477:29:477:33 | value : | semmle.label | value : | +| hash_flow.rb:479:14:479:18 | value | semmle.label | value | +| hash_flow.rb:482:10:482:10 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:482:10:482:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:489:15:489:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:492:9:492:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:492:9:492:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:492:9:496:7 | call to reject! [element :a] : | semmle.label | call to reject! [element :a] : | +| hash_flow.rb:492:30:492:34 | value : | semmle.label | value : | +| hash_flow.rb:494:14:494:18 | value | semmle.label | value | +| hash_flow.rb:497:10:497:10 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:497:10:497:14 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:498:10:498:13 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:498:10:498:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:505:15:505:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:507:15:507:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:512:5:512:9 | [post] hash2 [element :a] : | semmle.label | [post] hash2 [element :a] : | +| hash_flow.rb:512:5:512:9 | [post] hash2 [element :c] : | semmle.label | [post] hash2 [element :c] : | +| hash_flow.rb:512:19:512:22 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:512:19:512:22 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:513:10:513:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:513:11:513:15 | hash2 [element :a] : | semmle.label | hash2 [element :a] : | +| hash_flow.rb:513:11:513:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:515:10:515:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:515:11:515:15 | hash2 [element :c] : | semmle.label | hash2 [element :c] : | +| hash_flow.rb:515:11:515:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:520:15:520:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:522:15:522:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:524:9:524:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:524:9:524:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:524:9:528:7 | call to select [element :a] : | semmle.label | call to select [element :a] : | +| hash_flow.rb:524:30:524:34 | value : | semmle.label | value : | +| hash_flow.rb:526:14:526:18 | value | semmle.label | value | +| hash_flow.rb:529:10:529:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:529:11:529:11 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:529:11:529:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:536:15:536:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:538:15:538:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:540:5:540:8 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:540:5:540:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:540:5:540:8 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:540:27:540:31 | value : | semmle.label | value : | +| hash_flow.rb:542:14:542:18 | value | semmle.label | value | +| hash_flow.rb:545:10:545:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:545:11:545:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:545:11:545:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:552:15:552:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:554:15:554:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:556:9:556:12 | [post] hash [element :a] : | semmle.label | [post] hash [element :a] : | +| hash_flow.rb:556:9:556:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:556:9:556:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:556:9:556:18 | call to shift [element 1] : | semmle.label | call to shift [element 1] : | +| hash_flow.rb:557:10:557:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:557:11:557:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:557:11:557:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:559:10:559:15 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:559:11:559:11 | b [element 1] : | semmle.label | b [element 1] : | +| hash_flow.rb:559:11:559:14 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:566:15:566:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:568:15:568:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:570:9:570:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:570:9:570:26 | call to slice [element :a] : | semmle.label | call to slice [element :a] : | +| hash_flow.rb:571:10:571:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:571:11:571:11 | b [element :a] : | semmle.label | b [element :a] : | +| hash_flow.rb:571:11:571:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:575:9:575:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:575:9:575:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:575:9:575:25 | call to slice [element :a] : | semmle.label | call to slice [element :a] : | +| hash_flow.rb:575:9:575:25 | call to slice [element :c] : | semmle.label | call to slice [element :c] : | +| hash_flow.rb:576:10:576:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:576:11:576:11 | c [element :a] : | semmle.label | c [element :a] : | +| hash_flow.rb:576:11:576:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:578:10:578:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:578:11:578:11 | c [element :c] : | semmle.label | c [element :c] : | +| hash_flow.rb:578:11:578:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:585:15:585:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:587:15:587:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:589:9:589:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:589:9:589:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:589:9:589:17 | call to to_a [element, element 1] : | semmle.label | call to to_a [element, element 1] : | +| hash_flow.rb:591:10:591:18 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:591:11:591:11 | a [element, element 1] : | semmle.label | a [element, element 1] : | +| hash_flow.rb:591:11:591:14 | ...[...] [element 1] : | semmle.label | ...[...] [element 1] : | +| hash_flow.rb:591:11:591:17 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:598:15:598:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:600:15:600:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:602:9:602:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:602:9:602:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:602:9:602:17 | call to to_h [element :a] : | semmle.label | call to to_h [element :a] : | +| hash_flow.rb:602:9:602:17 | call to to_h [element :c] : | semmle.label | call to to_h [element :c] : | +| hash_flow.rb:603:10:603:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:603:11:603:11 | a [element :a] : | semmle.label | a [element :a] : | +| hash_flow.rb:603:11:603:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:605:10:605:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:605:11:605:11 | a [element :c] : | semmle.label | a [element :c] : | +| hash_flow.rb:605:11:605:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:607:9:607:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:607:9:607:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:607:9:611:7 | call to to_h [element] : | semmle.label | call to to_h [element] : | +| hash_flow.rb:607:28:607:32 | value : | semmle.label | value : | +| hash_flow.rb:609:14:609:18 | value | semmle.label | value | +| hash_flow.rb:610:14:610:24 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:612:10:612:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:612:11:612:11 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:612:11:612:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:619:15:619:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:621:15:621:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:623:9:623:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:623:9:623:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:623:9:623:45 | call to transform_keys [element] : | semmle.label | call to transform_keys [element] : | +| hash_flow.rb:624:10:624:17 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:624:11:624:11 | a [element] : | semmle.label | a [element] : | +| hash_flow.rb:624:11:624:16 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:625:10:625:17 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:625:11:625:11 | a [element] : | semmle.label | a [element] : | +| hash_flow.rb:625:11:625:16 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:626:10:626:17 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:626:11:626:11 | a [element] : | semmle.label | a [element] : | +| hash_flow.rb:626:11:626:16 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:633:15:633:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:635:15:635:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:637:5:637:8 | [post] hash [element] : | semmle.label | [post] hash [element] : | +| hash_flow.rb:637:5:637:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:637:5:637:8 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:638:10:638:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:638:11:638:14 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:638:11:638:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:639:10:639:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:639:11:639:14 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:639:11:639:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:640:10:640:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:640:11:640:14 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:640:11:640:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:647:15:647:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:649:15:649:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:651:9:651:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:651:9:651:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:651:9:654:7 | call to transform_values [element] : | semmle.label | call to transform_values [element] : | +| hash_flow.rb:651:35:651:39 | value : | semmle.label | value : | +| hash_flow.rb:652:14:652:18 | value | semmle.label | value | +| hash_flow.rb:653:9:653:19 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:655:10:655:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:655:11:655:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:655:11:655:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:656:10:656:16 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:656:11:656:11 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:656:11:656:15 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:663:15:663:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:665:15:665:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:667:5:667:8 | [post] hash [element] : | semmle.label | [post] hash [element] : | +| hash_flow.rb:667:5:667:8 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:667:5:667:8 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:667:32:667:36 | value : | semmle.label | value : | +| hash_flow.rb:668:14:668:18 | value | semmle.label | value | +| hash_flow.rb:669:9:669:19 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:671:10:671:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:671:11:671:14 | hash [element] : | semmle.label | hash [element] : | +| hash_flow.rb:671:11:671:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:678:15:678:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:680:15:680:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:683:15:683:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:685:15:685:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :a] : | semmle.label | [post] hash1 [element :a] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :c] : | semmle.label | [post] hash1 [element :c] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :d] : | semmle.label | [post] hash1 [element :d] : | +| hash_flow.rb:687:12:687:16 | [post] hash1 [element :f] : | semmle.label | [post] hash1 [element :f] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:687:12:687:16 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:687:12:691:7 | call to update [element :a] : | semmle.label | call to update [element :a] : | +| hash_flow.rb:687:12:691:7 | call to update [element :c] : | semmle.label | call to update [element :c] : | +| hash_flow.rb:687:12:691:7 | call to update [element :d] : | semmle.label | call to update [element :d] : | +| hash_flow.rb:687:12:691:7 | call to update [element :f] : | semmle.label | call to update [element :f] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | +| hash_flow.rb:687:25:687:29 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | +| hash_flow.rb:687:41:687:49 | old_value : | semmle.label | old_value : | +| hash_flow.rb:687:52:687:60 | new_value : | semmle.label | new_value : | +| hash_flow.rb:689:14:689:22 | old_value | semmle.label | old_value | +| hash_flow.rb:690:14:690:22 | new_value | semmle.label | new_value | +| hash_flow.rb:692:10:692:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:692:11:692:14 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:692:11:692:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:694:10:694:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:694:11:694:14 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:694:11:694:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:695:10:695:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:695:11:695:14 | hash [element :d] : | semmle.label | hash [element :d] : | +| hash_flow.rb:695:11:695:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:697:10:697:19 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:697:11:697:14 | hash [element :f] : | semmle.label | hash [element :f] : | +| hash_flow.rb:697:11:697:18 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:699:10:699:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:699:11:699:15 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:699:11:699:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:701:10:701:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:701:11:701:15 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:701:11:701:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:702:10:702:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:702:11:702:15 | hash1 [element :d] : | semmle.label | hash1 [element :d] : | +| hash_flow.rb:702:11:702:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:704:10:704:20 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:704:11:704:15 | hash1 [element :f] : | semmle.label | hash1 [element :f] : | +| hash_flow.rb:704:11:704:19 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:711:15:711:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:713:15:713:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:715:9:715:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:715:9:715:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:715:9:715:19 | call to values [element] : | semmle.label | call to values [element] : | +| hash_flow.rb:716:10:716:15 | ( ... ) | semmle.label | ( ... ) | +| hash_flow.rb:716:11:716:11 | a [element] : | semmle.label | a [element] : | +| hash_flow.rb:716:11:716:14 | ...[...] : | semmle.label | ...[...] : | +| hash_flow.rb:723:15:723:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:725:15:725:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:727:9:727:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:727:9:727:26 | call to values_at [element 0] : | semmle.label | call to values_at [element 0] : | +| hash_flow.rb:728:10:728:10 | b [element 0] : | semmle.label | b [element 0] : | +| hash_flow.rb:728:10:728:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:729:9:729:12 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:729:9:729:12 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:729:9:729:31 | call to fetch_values [element] : | semmle.label | call to fetch_values [element] : | +| hash_flow.rb:730:10:730:10 | b [element] : | semmle.label | b [element] : | +| hash_flow.rb:730:10:730:13 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:737:15:737:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:739:15:739:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:742:15:742:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:744:15:744:25 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:746:14:746:20 | ** ... [element :a] : | semmle.label | ** ... [element :a] : | +| hash_flow.rb:746:14:746:20 | ** ... [element :c] : | semmle.label | ** ... [element :c] : | +| hash_flow.rb:746:16:746:20 | hash1 [element :a] : | semmle.label | hash1 [element :a] : | +| hash_flow.rb:746:16:746:20 | hash1 [element :c] : | semmle.label | hash1 [element :c] : | +| hash_flow.rb:746:29:746:39 | call to taint : | semmle.label | call to taint : | +| hash_flow.rb:746:42:746:48 | ** ... [element :d] : | semmle.label | ** ... [element :d] : | +| hash_flow.rb:746:42:746:48 | ** ... [element :f] : | semmle.label | ** ... [element :f] : | +| hash_flow.rb:746:44:746:48 | hash2 [element :d] : | semmle.label | hash2 [element :d] : | +| hash_flow.rb:746:44:746:48 | hash2 [element :f] : | semmle.label | hash2 [element :f] : | +| hash_flow.rb:747:10:747:13 | hash [element :a] : | semmle.label | hash [element :a] : | +| hash_flow.rb:747:10:747:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:749:10:749:13 | hash [element :c] : | semmle.label | hash [element :c] : | +| hash_flow.rb:749:10:749:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:750:10:750:13 | hash [element :d] : | semmle.label | hash [element :d] : | +| hash_flow.rb:750:10:750:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:752:10:752:13 | hash [element :f] : | semmle.label | hash [element :f] : | +| hash_flow.rb:752:10:752:17 | ...[...] | semmle.label | ...[...] | +| hash_flow.rb:753:10:753:13 | hash [element :g] : | semmle.label | hash [element :g] : | +| hash_flow.rb:753:10:753:17 | ...[...] | semmle.label | ...[...] | subpaths #select | hash_flow.rb:22:10:22:17 | ...[...] | hash_flow.rb:11:15:11:24 | call to taint : | hash_flow.rb:22:10:22:17 | ...[...] | $@ | hash_flow.rb:11:15:11:24 | call to taint : | call to taint : | @@ -1114,160 +1132,162 @@ subpaths | hash_flow.rb:65:10:65:18 | ...[...] | hash_flow.rb:64:24:64:33 | call to taint : | hash_flow.rb:65:10:65:18 | ...[...] | $@ | hash_flow.rb:64:24:64:33 | call to taint : | call to taint : | | hash_flow.rb:66:10:66:18 | ...[...] | hash_flow.rb:64:24:64:33 | call to taint : | hash_flow.rb:66:10:66:18 | ...[...] | $@ | hash_flow.rb:64:24:64:33 | call to taint : | call to taint : | | hash_flow.rb:69:10:69:18 | ...[...] | hash_flow.rb:68:22:68:31 | call to taint : | hash_flow.rb:69:10:69:18 | ...[...] | $@ | hash_flow.rb:68:22:68:31 | call to taint : | call to taint : | -| hash_flow.rb:77:10:77:18 | ...[...] | hash_flow.rb:76:26:76:35 | call to taint : | hash_flow.rb:77:10:77:18 | ...[...] | $@ | hash_flow.rb:76:26:76:35 | call to taint : | call to taint : | -| hash_flow.rb:89:10:89:18 | ...[...] | hash_flow.rb:85:15:85:24 | call to taint : | hash_flow.rb:89:10:89:18 | ...[...] | $@ | hash_flow.rb:85:15:85:24 | call to taint : | call to taint : | -| hash_flow.rb:98:10:98:10 | b | hash_flow.rb:97:21:97:30 | call to taint : | hash_flow.rb:98:10:98:10 | b | $@ | hash_flow.rb:97:21:97:30 | call to taint : | call to taint : | -| hash_flow.rb:106:10:106:17 | ...[...] | hash_flow.rb:105:24:105:33 | call to taint : | hash_flow.rb:106:10:106:17 | ...[...] | $@ | hash_flow.rb:105:24:105:33 | call to taint : | call to taint : | -| hash_flow.rb:107:10:107:10 | b | hash_flow.rb:105:24:105:33 | call to taint : | hash_flow.rb:107:10:107:10 | b | $@ | hash_flow.rb:105:24:105:33 | call to taint : | call to taint : | -| hash_flow.rb:111:10:111:17 | ...[...] | hash_flow.rb:110:23:110:32 | call to taint : | hash_flow.rb:111:10:111:17 | ...[...] | $@ | hash_flow.rb:110:23:110:32 | call to taint : | call to taint : | -| hash_flow.rb:112:10:112:17 | ...[...] | hash_flow.rb:110:23:110:32 | call to taint : | hash_flow.rb:112:10:112:17 | ...[...] | $@ | hash_flow.rb:110:23:110:32 | call to taint : | call to taint : | -| hash_flow.rb:113:10:113:10 | c | hash_flow.rb:110:23:110:32 | call to taint : | hash_flow.rb:113:10:113:10 | c | $@ | hash_flow.rb:110:23:110:32 | call to taint : | call to taint : | -| hash_flow.rb:124:14:124:25 | key_or_value | hash_flow.rb:120:15:120:24 | call to taint : | hash_flow.rb:124:14:124:25 | key_or_value | $@ | hash_flow.rb:120:15:120:24 | call to taint : | call to taint : | -| hash_flow.rb:128:14:128:18 | value | hash_flow.rb:120:15:120:24 | call to taint : | hash_flow.rb:128:14:128:18 | value | $@ | hash_flow.rb:120:15:120:24 | call to taint : | call to taint : | -| hash_flow.rb:141:10:141:13 | ...[...] | hash_flow.rb:136:15:136:25 | call to taint : | hash_flow.rb:141:10:141:13 | ...[...] | $@ | hash_flow.rb:136:15:136:25 | call to taint : | call to taint : | -| hash_flow.rb:142:10:142:13 | ...[...] | hash_flow.rb:136:15:136:25 | call to taint : | hash_flow.rb:142:10:142:13 | ...[...] | $@ | hash_flow.rb:136:15:136:25 | call to taint : | call to taint : | -| hash_flow.rb:144:10:144:13 | ...[...] | hash_flow.rb:136:15:136:25 | call to taint : | hash_flow.rb:144:10:144:13 | ...[...] | $@ | hash_flow.rb:136:15:136:25 | call to taint : | call to taint : | -| hash_flow.rb:166:10:166:14 | ...[...] | hash_flow.rb:162:15:162:25 | call to taint : | hash_flow.rb:166:10:166:14 | ...[...] | $@ | hash_flow.rb:162:15:162:25 | call to taint : | call to taint : | -| hash_flow.rb:178:10:178:10 | a | hash_flow.rb:174:15:174:25 | call to taint : | hash_flow.rb:178:10:178:10 | a | $@ | hash_flow.rb:174:15:174:25 | call to taint : | call to taint : | -| hash_flow.rb:191:14:191:18 | value | hash_flow.rb:186:15:186:25 | call to taint : | hash_flow.rb:191:14:191:18 | value | $@ | hash_flow.rb:186:15:186:25 | call to taint : | call to taint : | -| hash_flow.rb:193:10:193:14 | ...[...] | hash_flow.rb:186:15:186:25 | call to taint : | hash_flow.rb:193:10:193:14 | ...[...] | $@ | hash_flow.rb:186:15:186:25 | call to taint : | call to taint : | -| hash_flow.rb:194:10:194:17 | ...[...] | hash_flow.rb:186:15:186:25 | call to taint : | hash_flow.rb:194:10:194:17 | ...[...] | $@ | hash_flow.rb:186:15:186:25 | call to taint : | call to taint : | -| hash_flow.rb:209:10:209:21 | call to dig | hash_flow.rb:202:15:202:25 | call to taint : | hash_flow.rb:209:10:209:21 | call to dig | $@ | hash_flow.rb:202:15:202:25 | call to taint : | call to taint : | -| hash_flow.rb:211:10:211:24 | call to dig | hash_flow.rb:205:19:205:29 | call to taint : | hash_flow.rb:211:10:211:24 | call to dig | $@ | hash_flow.rb:205:19:205:29 | call to taint : | call to taint : | -| hash_flow.rb:224:14:224:18 | value | hash_flow.rb:219:15:219:25 | call to taint : | hash_flow.rb:224:14:224:18 | value | $@ | hash_flow.rb:219:15:219:25 | call to taint : | call to taint : | -| hash_flow.rb:226:10:226:14 | ...[...] | hash_flow.rb:219:15:219:25 | call to taint : | hash_flow.rb:226:10:226:14 | ...[...] | $@ | hash_flow.rb:219:15:219:25 | call to taint : | call to taint : | -| hash_flow.rb:240:10:240:14 | ...[...] | hash_flow.rb:234:15:234:25 | call to taint : | hash_flow.rb:240:10:240:14 | ...[...] | $@ | hash_flow.rb:234:15:234:25 | call to taint : | call to taint : | -| hash_flow.rb:253:14:253:18 | value | hash_flow.rb:248:15:248:25 | call to taint : | hash_flow.rb:253:14:253:18 | value | $@ | hash_flow.rb:248:15:248:25 | call to taint : | call to taint : | -| hash_flow.rb:255:10:255:14 | ...[...] | hash_flow.rb:248:15:248:25 | call to taint : | hash_flow.rb:255:10:255:14 | ...[...] | $@ | hash_flow.rb:248:15:248:25 | call to taint : | call to taint : | -| hash_flow.rb:267:14:267:18 | value | hash_flow.rb:263:15:263:25 | call to taint : | hash_flow.rb:267:14:267:18 | value | $@ | hash_flow.rb:263:15:263:25 | call to taint : | call to taint : | -| hash_flow.rb:269:10:269:14 | ...[...] | hash_flow.rb:263:15:263:25 | call to taint : | hash_flow.rb:269:10:269:14 | ...[...] | $@ | hash_flow.rb:263:15:263:25 | call to taint : | call to taint : | -| hash_flow.rb:285:10:285:14 | ...[...] | hash_flow.rb:279:15:279:25 | call to taint : | hash_flow.rb:285:10:285:14 | ...[...] | $@ | hash_flow.rb:279:15:279:25 | call to taint : | call to taint : | -| hash_flow.rb:298:14:298:14 | x | hash_flow.rb:297:20:297:30 | call to taint : | hash_flow.rb:298:14:298:14 | x | $@ | hash_flow.rb:297:20:297:30 | call to taint : | call to taint : | -| hash_flow.rb:300:10:300:10 | b | hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:300:10:300:10 | b | $@ | hash_flow.rb:293:15:293:25 | call to taint : | call to taint : | -| hash_flow.rb:300:10:300:10 | b | hash_flow.rb:295:15:295:25 | call to taint : | hash_flow.rb:300:10:300:10 | b | $@ | hash_flow.rb:295:15:295:25 | call to taint : | call to taint : | -| hash_flow.rb:302:10:302:10 | b | hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:302:10:302:10 | b | $@ | hash_flow.rb:293:15:293:25 | call to taint : | call to taint : | -| hash_flow.rb:304:10:304:10 | b | hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:304:10:304:10 | b | $@ | hash_flow.rb:293:15:293:25 | call to taint : | call to taint : | -| hash_flow.rb:304:10:304:10 | b | hash_flow.rb:303:24:303:34 | call to taint : | hash_flow.rb:304:10:304:10 | b | $@ | hash_flow.rb:303:24:303:34 | call to taint : | call to taint : | -| hash_flow.rb:306:10:306:10 | b | hash_flow.rb:305:24:305:34 | call to taint : | hash_flow.rb:306:10:306:10 | b | $@ | hash_flow.rb:305:24:305:34 | call to taint : | call to taint : | -| hash_flow.rb:308:10:308:10 | b | hash_flow.rb:293:15:293:25 | call to taint : | hash_flow.rb:308:10:308:10 | b | $@ | hash_flow.rb:293:15:293:25 | call to taint : | call to taint : | -| hash_flow.rb:308:10:308:10 | b | hash_flow.rb:295:15:295:25 | call to taint : | hash_flow.rb:308:10:308:10 | b | $@ | hash_flow.rb:295:15:295:25 | call to taint : | call to taint : | -| hash_flow.rb:308:10:308:10 | b | hash_flow.rb:307:23:307:33 | call to taint : | hash_flow.rb:308:10:308:10 | b | $@ | hash_flow.rb:307:23:307:33 | call to taint : | call to taint : | -| hash_flow.rb:320:14:320:14 | x | hash_flow.rb:319:27:319:37 | call to taint : | hash_flow.rb:320:14:320:14 | x | $@ | hash_flow.rb:319:27:319:37 | call to taint : | call to taint : | -| hash_flow.rb:323:10:323:13 | ...[...] | hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:323:10:323:13 | ...[...] | $@ | hash_flow.rb:315:15:315:25 | call to taint : | call to taint : | -| hash_flow.rb:323:10:323:13 | ...[...] | hash_flow.rb:317:15:317:25 | call to taint : | hash_flow.rb:323:10:323:13 | ...[...] | $@ | hash_flow.rb:317:15:317:25 | call to taint : | call to taint : | -| hash_flow.rb:323:10:323:13 | ...[...] | hash_flow.rb:321:9:321:19 | call to taint : | hash_flow.rb:323:10:323:13 | ...[...] | $@ | hash_flow.rb:321:9:321:19 | call to taint : | call to taint : | -| hash_flow.rb:325:10:325:13 | ...[...] | hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:325:10:325:13 | ...[...] | $@ | hash_flow.rb:315:15:315:25 | call to taint : | call to taint : | -| hash_flow.rb:327:10:327:13 | ...[...] | hash_flow.rb:315:15:315:25 | call to taint : | hash_flow.rb:327:10:327:13 | ...[...] | $@ | hash_flow.rb:315:15:315:25 | call to taint : | call to taint : | -| hash_flow.rb:327:10:327:13 | ...[...] | hash_flow.rb:317:15:317:25 | call to taint : | hash_flow.rb:327:10:327:13 | ...[...] | $@ | hash_flow.rb:317:15:317:25 | call to taint : | call to taint : | -| hash_flow.rb:340:14:340:18 | value | hash_flow.rb:334:15:334:25 | call to taint : | hash_flow.rb:340:14:340:18 | value | $@ | hash_flow.rb:334:15:334:25 | call to taint : | call to taint : | -| hash_flow.rb:340:14:340:18 | value | hash_flow.rb:336:15:336:25 | call to taint : | hash_flow.rb:340:14:340:18 | value | $@ | hash_flow.rb:336:15:336:25 | call to taint : | call to taint : | -| hash_flow.rb:343:10:343:16 | ( ... ) | hash_flow.rb:334:15:334:25 | call to taint : | hash_flow.rb:343:10:343:16 | ( ... ) | $@ | hash_flow.rb:334:15:334:25 | call to taint : | call to taint : | -| hash_flow.rb:356:14:356:18 | value | hash_flow.rb:350:15:350:25 | call to taint : | hash_flow.rb:356:14:356:18 | value | $@ | hash_flow.rb:350:15:350:25 | call to taint : | call to taint : | -| hash_flow.rb:356:14:356:18 | value | hash_flow.rb:352:15:352:25 | call to taint : | hash_flow.rb:356:14:356:18 | value | $@ | hash_flow.rb:352:15:352:25 | call to taint : | call to taint : | -| hash_flow.rb:359:10:359:19 | ( ... ) | hash_flow.rb:350:15:350:25 | call to taint : | hash_flow.rb:359:10:359:19 | ( ... ) | $@ | hash_flow.rb:350:15:350:25 | call to taint : | call to taint : | -| hash_flow.rb:371:10:371:15 | ( ... ) | hash_flow.rb:366:15:366:25 | call to taint : | hash_flow.rb:371:10:371:15 | ( ... ) | $@ | hash_flow.rb:366:15:366:25 | call to taint : | call to taint : | -| hash_flow.rb:371:10:371:15 | ( ... ) | hash_flow.rb:368:15:368:25 | call to taint : | hash_flow.rb:371:10:371:15 | ( ... ) | $@ | hash_flow.rb:368:15:368:25 | call to taint : | call to taint : | -| hash_flow.rb:384:14:384:18 | value | hash_flow.rb:378:15:378:25 | call to taint : | hash_flow.rb:384:14:384:18 | value | $@ | hash_flow.rb:378:15:378:25 | call to taint : | call to taint : | -| hash_flow.rb:384:14:384:18 | value | hash_flow.rb:380:15:380:25 | call to taint : | hash_flow.rb:384:14:384:18 | value | $@ | hash_flow.rb:380:15:380:25 | call to taint : | call to taint : | -| hash_flow.rb:387:10:387:19 | ( ... ) | hash_flow.rb:378:15:378:25 | call to taint : | hash_flow.rb:387:10:387:19 | ( ... ) | $@ | hash_flow.rb:378:15:378:25 | call to taint : | call to taint : | -| hash_flow.rb:388:10:388:16 | ( ... ) | hash_flow.rb:378:15:378:25 | call to taint : | hash_flow.rb:388:10:388:16 | ( ... ) | $@ | hash_flow.rb:378:15:378:25 | call to taint : | call to taint : | -| hash_flow.rb:406:14:406:22 | old_value | hash_flow.rb:395:15:395:25 | call to taint : | hash_flow.rb:406:14:406:22 | old_value | $@ | hash_flow.rb:395:15:395:25 | call to taint : | call to taint : | -| hash_flow.rb:406:14:406:22 | old_value | hash_flow.rb:397:15:397:25 | call to taint : | hash_flow.rb:406:14:406:22 | old_value | $@ | hash_flow.rb:397:15:397:25 | call to taint : | call to taint : | -| hash_flow.rb:406:14:406:22 | old_value | hash_flow.rb:400:15:400:25 | call to taint : | hash_flow.rb:406:14:406:22 | old_value | $@ | hash_flow.rb:400:15:400:25 | call to taint : | call to taint : | -| hash_flow.rb:406:14:406:22 | old_value | hash_flow.rb:402:15:402:25 | call to taint : | hash_flow.rb:406:14:406:22 | old_value | $@ | hash_flow.rb:402:15:402:25 | call to taint : | call to taint : | -| hash_flow.rb:407:14:407:22 | new_value | hash_flow.rb:395:15:395:25 | call to taint : | hash_flow.rb:407:14:407:22 | new_value | $@ | hash_flow.rb:395:15:395:25 | call to taint : | call to taint : | -| hash_flow.rb:407:14:407:22 | new_value | hash_flow.rb:397:15:397:25 | call to taint : | hash_flow.rb:407:14:407:22 | new_value | $@ | hash_flow.rb:397:15:397:25 | call to taint : | call to taint : | -| hash_flow.rb:407:14:407:22 | new_value | hash_flow.rb:400:15:400:25 | call to taint : | hash_flow.rb:407:14:407:22 | new_value | $@ | hash_flow.rb:400:15:400:25 | call to taint : | call to taint : | -| hash_flow.rb:407:14:407:22 | new_value | hash_flow.rb:402:15:402:25 | call to taint : | hash_flow.rb:407:14:407:22 | new_value | $@ | hash_flow.rb:402:15:402:25 | call to taint : | call to taint : | -| hash_flow.rb:409:10:409:19 | ( ... ) | hash_flow.rb:395:15:395:25 | call to taint : | hash_flow.rb:409:10:409:19 | ( ... ) | $@ | hash_flow.rb:395:15:395:25 | call to taint : | call to taint : | -| hash_flow.rb:411:10:411:19 | ( ... ) | hash_flow.rb:397:15:397:25 | call to taint : | hash_flow.rb:411:10:411:19 | ( ... ) | $@ | hash_flow.rb:397:15:397:25 | call to taint : | call to taint : | -| hash_flow.rb:412:10:412:19 | ( ... ) | hash_flow.rb:400:15:400:25 | call to taint : | hash_flow.rb:412:10:412:19 | ( ... ) | $@ | hash_flow.rb:400:15:400:25 | call to taint : | call to taint : | -| hash_flow.rb:414:10:414:19 | ( ... ) | hash_flow.rb:402:15:402:25 | call to taint : | hash_flow.rb:414:10:414:19 | ( ... ) | $@ | hash_flow.rb:402:15:402:25 | call to taint : | call to taint : | -| hash_flow.rb:432:14:432:22 | old_value | hash_flow.rb:421:15:421:25 | call to taint : | hash_flow.rb:432:14:432:22 | old_value | $@ | hash_flow.rb:421:15:421:25 | call to taint : | call to taint : | -| hash_flow.rb:432:14:432:22 | old_value | hash_flow.rb:423:15:423:25 | call to taint : | hash_flow.rb:432:14:432:22 | old_value | $@ | hash_flow.rb:423:15:423:25 | call to taint : | call to taint : | -| hash_flow.rb:432:14:432:22 | old_value | hash_flow.rb:426:15:426:25 | call to taint : | hash_flow.rb:432:14:432:22 | old_value | $@ | hash_flow.rb:426:15:426:25 | call to taint : | call to taint : | -| hash_flow.rb:432:14:432:22 | old_value | hash_flow.rb:428:15:428:25 | call to taint : | hash_flow.rb:432:14:432:22 | old_value | $@ | hash_flow.rb:428:15:428:25 | call to taint : | call to taint : | -| hash_flow.rb:433:14:433:22 | new_value | hash_flow.rb:421:15:421:25 | call to taint : | hash_flow.rb:433:14:433:22 | new_value | $@ | hash_flow.rb:421:15:421:25 | call to taint : | call to taint : | -| hash_flow.rb:433:14:433:22 | new_value | hash_flow.rb:423:15:423:25 | call to taint : | hash_flow.rb:433:14:433:22 | new_value | $@ | hash_flow.rb:423:15:423:25 | call to taint : | call to taint : | -| hash_flow.rb:433:14:433:22 | new_value | hash_flow.rb:426:15:426:25 | call to taint : | hash_flow.rb:433:14:433:22 | new_value | $@ | hash_flow.rb:426:15:426:25 | call to taint : | call to taint : | -| hash_flow.rb:433:14:433:22 | new_value | hash_flow.rb:428:15:428:25 | call to taint : | hash_flow.rb:433:14:433:22 | new_value | $@ | hash_flow.rb:428:15:428:25 | call to taint : | call to taint : | -| hash_flow.rb:435:10:435:19 | ( ... ) | hash_flow.rb:421:15:421:25 | call to taint : | hash_flow.rb:435:10:435:19 | ( ... ) | $@ | hash_flow.rb:421:15:421:25 | call to taint : | call to taint : | -| hash_flow.rb:437:10:437:19 | ( ... ) | hash_flow.rb:423:15:423:25 | call to taint : | hash_flow.rb:437:10:437:19 | ( ... ) | $@ | hash_flow.rb:423:15:423:25 | call to taint : | call to taint : | -| hash_flow.rb:438:10:438:19 | ( ... ) | hash_flow.rb:426:15:426:25 | call to taint : | hash_flow.rb:438:10:438:19 | ( ... ) | $@ | hash_flow.rb:426:15:426:25 | call to taint : | call to taint : | -| hash_flow.rb:440:10:440:19 | ( ... ) | hash_flow.rb:428:15:428:25 | call to taint : | hash_flow.rb:440:10:440:19 | ( ... ) | $@ | hash_flow.rb:428:15:428:25 | call to taint : | call to taint : | -| hash_flow.rb:442:10:442:20 | ( ... ) | hash_flow.rb:421:15:421:25 | call to taint : | hash_flow.rb:442:10:442:20 | ( ... ) | $@ | hash_flow.rb:421:15:421:25 | call to taint : | call to taint : | -| hash_flow.rb:444:10:444:20 | ( ... ) | hash_flow.rb:423:15:423:25 | call to taint : | hash_flow.rb:444:10:444:20 | ( ... ) | $@ | hash_flow.rb:423:15:423:25 | call to taint : | call to taint : | -| hash_flow.rb:445:10:445:20 | ( ... ) | hash_flow.rb:426:15:426:25 | call to taint : | hash_flow.rb:445:10:445:20 | ( ... ) | $@ | hash_flow.rb:426:15:426:25 | call to taint : | call to taint : | -| hash_flow.rb:447:10:447:20 | ( ... ) | hash_flow.rb:428:15:428:25 | call to taint : | hash_flow.rb:447:10:447:20 | ( ... ) | $@ | hash_flow.rb:428:15:428:25 | call to taint : | call to taint : | -| hash_flow.rb:459:10:459:13 | ...[...] | hash_flow.rb:454:15:454:25 | call to taint : | hash_flow.rb:459:10:459:13 | ...[...] | $@ | hash_flow.rb:454:15:454:25 | call to taint : | call to taint : | -| hash_flow.rb:471:14:471:18 | value | hash_flow.rb:466:15:466:25 | call to taint : | hash_flow.rb:471:14:471:18 | value | $@ | hash_flow.rb:466:15:466:25 | call to taint : | call to taint : | -| hash_flow.rb:474:10:474:14 | ...[...] | hash_flow.rb:466:15:466:25 | call to taint : | hash_flow.rb:474:10:474:14 | ...[...] | $@ | hash_flow.rb:466:15:466:25 | call to taint : | call to taint : | -| hash_flow.rb:486:14:486:18 | value | hash_flow.rb:481:15:481:25 | call to taint : | hash_flow.rb:486:14:486:18 | value | $@ | hash_flow.rb:481:15:481:25 | call to taint : | call to taint : | -| hash_flow.rb:489:10:489:14 | ...[...] | hash_flow.rb:481:15:481:25 | call to taint : | hash_flow.rb:489:10:489:14 | ...[...] | $@ | hash_flow.rb:481:15:481:25 | call to taint : | call to taint : | -| hash_flow.rb:490:10:490:17 | ...[...] | hash_flow.rb:481:15:481:25 | call to taint : | hash_flow.rb:490:10:490:17 | ...[...] | $@ | hash_flow.rb:481:15:481:25 | call to taint : | call to taint : | -| hash_flow.rb:505:10:505:20 | ( ... ) | hash_flow.rb:497:15:497:25 | call to taint : | hash_flow.rb:505:10:505:20 | ( ... ) | $@ | hash_flow.rb:497:15:497:25 | call to taint : | call to taint : | -| hash_flow.rb:507:10:507:20 | ( ... ) | hash_flow.rb:499:15:499:25 | call to taint : | hash_flow.rb:507:10:507:20 | ( ... ) | $@ | hash_flow.rb:499:15:499:25 | call to taint : | call to taint : | -| hash_flow.rb:518:14:518:18 | value | hash_flow.rb:512:15:512:25 | call to taint : | hash_flow.rb:518:14:518:18 | value | $@ | hash_flow.rb:512:15:512:25 | call to taint : | call to taint : | -| hash_flow.rb:518:14:518:18 | value | hash_flow.rb:514:15:514:25 | call to taint : | hash_flow.rb:518:14:518:18 | value | $@ | hash_flow.rb:514:15:514:25 | call to taint : | call to taint : | -| hash_flow.rb:521:10:521:16 | ( ... ) | hash_flow.rb:512:15:512:25 | call to taint : | hash_flow.rb:521:10:521:16 | ( ... ) | $@ | hash_flow.rb:512:15:512:25 | call to taint : | call to taint : | -| hash_flow.rb:534:14:534:18 | value | hash_flow.rb:528:15:528:25 | call to taint : | hash_flow.rb:534:14:534:18 | value | $@ | hash_flow.rb:528:15:528:25 | call to taint : | call to taint : | -| hash_flow.rb:534:14:534:18 | value | hash_flow.rb:530:15:530:25 | call to taint : | hash_flow.rb:534:14:534:18 | value | $@ | hash_flow.rb:530:15:530:25 | call to taint : | call to taint : | -| hash_flow.rb:537:10:537:19 | ( ... ) | hash_flow.rb:528:15:528:25 | call to taint : | hash_flow.rb:537:10:537:19 | ( ... ) | $@ | hash_flow.rb:528:15:528:25 | call to taint : | call to taint : | -| hash_flow.rb:549:10:549:19 | ( ... ) | hash_flow.rb:544:15:544:25 | call to taint : | hash_flow.rb:549:10:549:19 | ( ... ) | $@ | hash_flow.rb:544:15:544:25 | call to taint : | call to taint : | -| hash_flow.rb:551:10:551:15 | ( ... ) | hash_flow.rb:544:15:544:25 | call to taint : | hash_flow.rb:551:10:551:15 | ( ... ) | $@ | hash_flow.rb:544:15:544:25 | call to taint : | call to taint : | -| hash_flow.rb:551:10:551:15 | ( ... ) | hash_flow.rb:546:15:546:25 | call to taint : | hash_flow.rb:551:10:551:15 | ( ... ) | $@ | hash_flow.rb:546:15:546:25 | call to taint : | call to taint : | -| hash_flow.rb:563:10:563:16 | ( ... ) | hash_flow.rb:558:15:558:25 | call to taint : | hash_flow.rb:563:10:563:16 | ( ... ) | $@ | hash_flow.rb:558:15:558:25 | call to taint : | call to taint : | -| hash_flow.rb:568:10:568:16 | ( ... ) | hash_flow.rb:558:15:558:25 | call to taint : | hash_flow.rb:568:10:568:16 | ( ... ) | $@ | hash_flow.rb:558:15:558:25 | call to taint : | call to taint : | -| hash_flow.rb:570:10:570:16 | ( ... ) | hash_flow.rb:560:15:560:25 | call to taint : | hash_flow.rb:570:10:570:16 | ( ... ) | $@ | hash_flow.rb:560:15:560:25 | call to taint : | call to taint : | -| hash_flow.rb:583:10:583:18 | ( ... ) | hash_flow.rb:577:15:577:25 | call to taint : | hash_flow.rb:583:10:583:18 | ( ... ) | $@ | hash_flow.rb:577:15:577:25 | call to taint : | call to taint : | -| hash_flow.rb:583:10:583:18 | ( ... ) | hash_flow.rb:579:15:579:25 | call to taint : | hash_flow.rb:583:10:583:18 | ( ... ) | $@ | hash_flow.rb:579:15:579:25 | call to taint : | call to taint : | -| hash_flow.rb:595:10:595:16 | ( ... ) | hash_flow.rb:590:15:590:25 | call to taint : | hash_flow.rb:595:10:595:16 | ( ... ) | $@ | hash_flow.rb:590:15:590:25 | call to taint : | call to taint : | -| hash_flow.rb:597:10:597:16 | ( ... ) | hash_flow.rb:592:15:592:25 | call to taint : | hash_flow.rb:597:10:597:16 | ( ... ) | $@ | hash_flow.rb:592:15:592:25 | call to taint : | call to taint : | -| hash_flow.rb:601:14:601:18 | value | hash_flow.rb:590:15:590:25 | call to taint : | hash_flow.rb:601:14:601:18 | value | $@ | hash_flow.rb:590:15:590:25 | call to taint : | call to taint : | -| hash_flow.rb:601:14:601:18 | value | hash_flow.rb:592:15:592:25 | call to taint : | hash_flow.rb:601:14:601:18 | value | $@ | hash_flow.rb:592:15:592:25 | call to taint : | call to taint : | -| hash_flow.rb:604:10:604:16 | ( ... ) | hash_flow.rb:602:14:602:24 | call to taint : | hash_flow.rb:604:10:604:16 | ( ... ) | $@ | hash_flow.rb:602:14:602:24 | call to taint : | call to taint : | -| hash_flow.rb:616:10:616:17 | ( ... ) | hash_flow.rb:611:15:611:25 | call to taint : | hash_flow.rb:616:10:616:17 | ( ... ) | $@ | hash_flow.rb:611:15:611:25 | call to taint : | call to taint : | -| hash_flow.rb:616:10:616:17 | ( ... ) | hash_flow.rb:613:15:613:25 | call to taint : | hash_flow.rb:616:10:616:17 | ( ... ) | $@ | hash_flow.rb:613:15:613:25 | call to taint : | call to taint : | -| hash_flow.rb:617:10:617:17 | ( ... ) | hash_flow.rb:611:15:611:25 | call to taint : | hash_flow.rb:617:10:617:17 | ( ... ) | $@ | hash_flow.rb:611:15:611:25 | call to taint : | call to taint : | -| hash_flow.rb:617:10:617:17 | ( ... ) | hash_flow.rb:613:15:613:25 | call to taint : | hash_flow.rb:617:10:617:17 | ( ... ) | $@ | hash_flow.rb:613:15:613:25 | call to taint : | call to taint : | -| hash_flow.rb:618:10:618:17 | ( ... ) | hash_flow.rb:611:15:611:25 | call to taint : | hash_flow.rb:618:10:618:17 | ( ... ) | $@ | hash_flow.rb:611:15:611:25 | call to taint : | call to taint : | -| hash_flow.rb:618:10:618:17 | ( ... ) | hash_flow.rb:613:15:613:25 | call to taint : | hash_flow.rb:618:10:618:17 | ( ... ) | $@ | hash_flow.rb:613:15:613:25 | call to taint : | call to taint : | -| hash_flow.rb:630:10:630:20 | ( ... ) | hash_flow.rb:625:15:625:25 | call to taint : | hash_flow.rb:630:10:630:20 | ( ... ) | $@ | hash_flow.rb:625:15:625:25 | call to taint : | call to taint : | -| hash_flow.rb:630:10:630:20 | ( ... ) | hash_flow.rb:627:15:627:25 | call to taint : | hash_flow.rb:630:10:630:20 | ( ... ) | $@ | hash_flow.rb:627:15:627:25 | call to taint : | call to taint : | -| hash_flow.rb:631:10:631:20 | ( ... ) | hash_flow.rb:625:15:625:25 | call to taint : | hash_flow.rb:631:10:631:20 | ( ... ) | $@ | hash_flow.rb:625:15:625:25 | call to taint : | call to taint : | -| hash_flow.rb:631:10:631:20 | ( ... ) | hash_flow.rb:627:15:627:25 | call to taint : | hash_flow.rb:631:10:631:20 | ( ... ) | $@ | hash_flow.rb:627:15:627:25 | call to taint : | call to taint : | -| hash_flow.rb:632:10:632:20 | ( ... ) | hash_flow.rb:625:15:625:25 | call to taint : | hash_flow.rb:632:10:632:20 | ( ... ) | $@ | hash_flow.rb:625:15:625:25 | call to taint : | call to taint : | -| hash_flow.rb:632:10:632:20 | ( ... ) | hash_flow.rb:627:15:627:25 | call to taint : | hash_flow.rb:632:10:632:20 | ( ... ) | $@ | hash_flow.rb:627:15:627:25 | call to taint : | call to taint : | -| hash_flow.rb:644:14:644:18 | value | hash_flow.rb:639:15:639:25 | call to taint : | hash_flow.rb:644:14:644:18 | value | $@ | hash_flow.rb:639:15:639:25 | call to taint : | call to taint : | -| hash_flow.rb:644:14:644:18 | value | hash_flow.rb:641:15:641:25 | call to taint : | hash_flow.rb:644:14:644:18 | value | $@ | hash_flow.rb:641:15:641:25 | call to taint : | call to taint : | -| hash_flow.rb:647:10:647:19 | ( ... ) | hash_flow.rb:639:15:639:25 | call to taint : | hash_flow.rb:647:10:647:19 | ( ... ) | $@ | hash_flow.rb:639:15:639:25 | call to taint : | call to taint : | -| hash_flow.rb:648:10:648:16 | ( ... ) | hash_flow.rb:645:9:645:19 | call to taint : | hash_flow.rb:648:10:648:16 | ( ... ) | $@ | hash_flow.rb:645:9:645:19 | call to taint : | call to taint : | -| hash_flow.rb:660:14:660:18 | value | hash_flow.rb:655:15:655:25 | call to taint : | hash_flow.rb:660:14:660:18 | value | $@ | hash_flow.rb:655:15:655:25 | call to taint : | call to taint : | -| hash_flow.rb:660:14:660:18 | value | hash_flow.rb:657:15:657:25 | call to taint : | hash_flow.rb:660:14:660:18 | value | $@ | hash_flow.rb:657:15:657:25 | call to taint : | call to taint : | -| hash_flow.rb:663:10:663:19 | ( ... ) | hash_flow.rb:661:9:661:19 | call to taint : | hash_flow.rb:663:10:663:19 | ( ... ) | $@ | hash_flow.rb:661:9:661:19 | call to taint : | call to taint : | -| hash_flow.rb:681:14:681:22 | old_value | hash_flow.rb:670:15:670:25 | call to taint : | hash_flow.rb:681:14:681:22 | old_value | $@ | hash_flow.rb:670:15:670:25 | call to taint : | call to taint : | -| hash_flow.rb:681:14:681:22 | old_value | hash_flow.rb:672:15:672:25 | call to taint : | hash_flow.rb:681:14:681:22 | old_value | $@ | hash_flow.rb:672:15:672:25 | call to taint : | call to taint : | -| hash_flow.rb:681:14:681:22 | old_value | hash_flow.rb:675:15:675:25 | call to taint : | hash_flow.rb:681:14:681:22 | old_value | $@ | hash_flow.rb:675:15:675:25 | call to taint : | call to taint : | -| hash_flow.rb:681:14:681:22 | old_value | hash_flow.rb:677:15:677:25 | call to taint : | hash_flow.rb:681:14:681:22 | old_value | $@ | hash_flow.rb:677:15:677:25 | call to taint : | call to taint : | -| hash_flow.rb:682:14:682:22 | new_value | hash_flow.rb:670:15:670:25 | call to taint : | hash_flow.rb:682:14:682:22 | new_value | $@ | hash_flow.rb:670:15:670:25 | call to taint : | call to taint : | -| hash_flow.rb:682:14:682:22 | new_value | hash_flow.rb:672:15:672:25 | call to taint : | hash_flow.rb:682:14:682:22 | new_value | $@ | hash_flow.rb:672:15:672:25 | call to taint : | call to taint : | -| hash_flow.rb:682:14:682:22 | new_value | hash_flow.rb:675:15:675:25 | call to taint : | hash_flow.rb:682:14:682:22 | new_value | $@ | hash_flow.rb:675:15:675:25 | call to taint : | call to taint : | -| hash_flow.rb:682:14:682:22 | new_value | hash_flow.rb:677:15:677:25 | call to taint : | hash_flow.rb:682:14:682:22 | new_value | $@ | hash_flow.rb:677:15:677:25 | call to taint : | call to taint : | -| hash_flow.rb:684:10:684:19 | ( ... ) | hash_flow.rb:670:15:670:25 | call to taint : | hash_flow.rb:684:10:684:19 | ( ... ) | $@ | hash_flow.rb:670:15:670:25 | call to taint : | call to taint : | -| hash_flow.rb:686:10:686:19 | ( ... ) | hash_flow.rb:672:15:672:25 | call to taint : | hash_flow.rb:686:10:686:19 | ( ... ) | $@ | hash_flow.rb:672:15:672:25 | call to taint : | call to taint : | -| hash_flow.rb:687:10:687:19 | ( ... ) | hash_flow.rb:675:15:675:25 | call to taint : | hash_flow.rb:687:10:687:19 | ( ... ) | $@ | hash_flow.rb:675:15:675:25 | call to taint : | call to taint : | -| hash_flow.rb:689:10:689:19 | ( ... ) | hash_flow.rb:677:15:677:25 | call to taint : | hash_flow.rb:689:10:689:19 | ( ... ) | $@ | hash_flow.rb:677:15:677:25 | call to taint : | call to taint : | -| hash_flow.rb:691:10:691:20 | ( ... ) | hash_flow.rb:670:15:670:25 | call to taint : | hash_flow.rb:691:10:691:20 | ( ... ) | $@ | hash_flow.rb:670:15:670:25 | call to taint : | call to taint : | -| hash_flow.rb:693:10:693:20 | ( ... ) | hash_flow.rb:672:15:672:25 | call to taint : | hash_flow.rb:693:10:693:20 | ( ... ) | $@ | hash_flow.rb:672:15:672:25 | call to taint : | call to taint : | -| hash_flow.rb:694:10:694:20 | ( ... ) | hash_flow.rb:675:15:675:25 | call to taint : | hash_flow.rb:694:10:694:20 | ( ... ) | $@ | hash_flow.rb:675:15:675:25 | call to taint : | call to taint : | -| hash_flow.rb:696:10:696:20 | ( ... ) | hash_flow.rb:677:15:677:25 | call to taint : | hash_flow.rb:696:10:696:20 | ( ... ) | $@ | hash_flow.rb:677:15:677:25 | call to taint : | call to taint : | -| hash_flow.rb:708:10:708:15 | ( ... ) | hash_flow.rb:703:15:703:25 | call to taint : | hash_flow.rb:708:10:708:15 | ( ... ) | $@ | hash_flow.rb:703:15:703:25 | call to taint : | call to taint : | -| hash_flow.rb:708:10:708:15 | ( ... ) | hash_flow.rb:705:15:705:25 | call to taint : | hash_flow.rb:708:10:708:15 | ( ... ) | $@ | hash_flow.rb:705:15:705:25 | call to taint : | call to taint : | -| hash_flow.rb:720:10:720:13 | ...[...] | hash_flow.rb:715:15:715:25 | call to taint : | hash_flow.rb:720:10:720:13 | ...[...] | $@ | hash_flow.rb:715:15:715:25 | call to taint : | call to taint : | -| hash_flow.rb:722:10:722:13 | ...[...] | hash_flow.rb:715:15:715:25 | call to taint : | hash_flow.rb:722:10:722:13 | ...[...] | $@ | hash_flow.rb:715:15:715:25 | call to taint : | call to taint : | -| hash_flow.rb:722:10:722:13 | ...[...] | hash_flow.rb:717:15:717:25 | call to taint : | hash_flow.rb:722:10:722:13 | ...[...] | $@ | hash_flow.rb:717:15:717:25 | call to taint : | call to taint : | -| hash_flow.rb:739:10:739:17 | ...[...] | hash_flow.rb:729:15:729:25 | call to taint : | hash_flow.rb:739:10:739:17 | ...[...] | $@ | hash_flow.rb:729:15:729:25 | call to taint : | call to taint : | -| hash_flow.rb:741:10:741:17 | ...[...] | hash_flow.rb:731:15:731:25 | call to taint : | hash_flow.rb:741:10:741:17 | ...[...] | $@ | hash_flow.rb:731:15:731:25 | call to taint : | call to taint : | -| hash_flow.rb:742:10:742:17 | ...[...] | hash_flow.rb:734:15:734:25 | call to taint : | hash_flow.rb:742:10:742:17 | ...[...] | $@ | hash_flow.rb:734:15:734:25 | call to taint : | call to taint : | -| hash_flow.rb:744:10:744:17 | ...[...] | hash_flow.rb:736:15:736:25 | call to taint : | hash_flow.rb:744:10:744:17 | ...[...] | $@ | hash_flow.rb:736:15:736:25 | call to taint : | call to taint : | -| hash_flow.rb:745:10:745:17 | ...[...] | hash_flow.rb:738:29:738:39 | call to taint : | hash_flow.rb:745:10:745:17 | ...[...] | $@ | hash_flow.rb:738:29:738:39 | call to taint : | call to taint : | +| hash_flow.rb:73:10:73:19 | ...[...] | hash_flow.rb:72:25:72:34 | call to taint : | hash_flow.rb:73:10:73:19 | ...[...] | $@ | hash_flow.rb:72:25:72:34 | call to taint : | call to taint : | +| hash_flow.rb:77:10:77:19 | ...[...] | hash_flow.rb:76:26:76:35 | call to taint : | hash_flow.rb:77:10:77:19 | ...[...] | $@ | hash_flow.rb:76:26:76:35 | call to taint : | call to taint : | +| hash_flow.rb:85:10:85:18 | ...[...] | hash_flow.rb:84:26:84:35 | call to taint : | hash_flow.rb:85:10:85:18 | ...[...] | $@ | hash_flow.rb:84:26:84:35 | call to taint : | call to taint : | +| hash_flow.rb:97:10:97:18 | ...[...] | hash_flow.rb:93:15:93:24 | call to taint : | hash_flow.rb:97:10:97:18 | ...[...] | $@ | hash_flow.rb:93:15:93:24 | call to taint : | call to taint : | +| hash_flow.rb:106:10:106:10 | b | hash_flow.rb:105:21:105:30 | call to taint : | hash_flow.rb:106:10:106:10 | b | $@ | hash_flow.rb:105:21:105:30 | call to taint : | call to taint : | +| hash_flow.rb:114:10:114:17 | ...[...] | hash_flow.rb:113:24:113:33 | call to taint : | hash_flow.rb:114:10:114:17 | ...[...] | $@ | hash_flow.rb:113:24:113:33 | call to taint : | call to taint : | +| hash_flow.rb:115:10:115:10 | b | hash_flow.rb:113:24:113:33 | call to taint : | hash_flow.rb:115:10:115:10 | b | $@ | hash_flow.rb:113:24:113:33 | call to taint : | call to taint : | +| hash_flow.rb:119:10:119:17 | ...[...] | hash_flow.rb:118:23:118:32 | call to taint : | hash_flow.rb:119:10:119:17 | ...[...] | $@ | hash_flow.rb:118:23:118:32 | call to taint : | call to taint : | +| hash_flow.rb:120:10:120:17 | ...[...] | hash_flow.rb:118:23:118:32 | call to taint : | hash_flow.rb:120:10:120:17 | ...[...] | $@ | hash_flow.rb:118:23:118:32 | call to taint : | call to taint : | +| hash_flow.rb:121:10:121:10 | c | hash_flow.rb:118:23:118:32 | call to taint : | hash_flow.rb:121:10:121:10 | c | $@ | hash_flow.rb:118:23:118:32 | call to taint : | call to taint : | +| hash_flow.rb:132:14:132:25 | key_or_value | hash_flow.rb:128:15:128:24 | call to taint : | hash_flow.rb:132:14:132:25 | key_or_value | $@ | hash_flow.rb:128:15:128:24 | call to taint : | call to taint : | +| hash_flow.rb:136:14:136:18 | value | hash_flow.rb:128:15:128:24 | call to taint : | hash_flow.rb:136:14:136:18 | value | $@ | hash_flow.rb:128:15:128:24 | call to taint : | call to taint : | +| hash_flow.rb:149:10:149:13 | ...[...] | hash_flow.rb:144:15:144:25 | call to taint : | hash_flow.rb:149:10:149:13 | ...[...] | $@ | hash_flow.rb:144:15:144:25 | call to taint : | call to taint : | +| hash_flow.rb:150:10:150:13 | ...[...] | hash_flow.rb:144:15:144:25 | call to taint : | hash_flow.rb:150:10:150:13 | ...[...] | $@ | hash_flow.rb:144:15:144:25 | call to taint : | call to taint : | +| hash_flow.rb:152:10:152:13 | ...[...] | hash_flow.rb:144:15:144:25 | call to taint : | hash_flow.rb:152:10:152:13 | ...[...] | $@ | hash_flow.rb:144:15:144:25 | call to taint : | call to taint : | +| hash_flow.rb:174:10:174:14 | ...[...] | hash_flow.rb:170:15:170:25 | call to taint : | hash_flow.rb:174:10:174:14 | ...[...] | $@ | hash_flow.rb:170:15:170:25 | call to taint : | call to taint : | +| hash_flow.rb:186:10:186:10 | a | hash_flow.rb:182:15:182:25 | call to taint : | hash_flow.rb:186:10:186:10 | a | $@ | hash_flow.rb:182:15:182:25 | call to taint : | call to taint : | +| hash_flow.rb:199:14:199:18 | value | hash_flow.rb:194:15:194:25 | call to taint : | hash_flow.rb:199:14:199:18 | value | $@ | hash_flow.rb:194:15:194:25 | call to taint : | call to taint : | +| hash_flow.rb:201:10:201:14 | ...[...] | hash_flow.rb:194:15:194:25 | call to taint : | hash_flow.rb:201:10:201:14 | ...[...] | $@ | hash_flow.rb:194:15:194:25 | call to taint : | call to taint : | +| hash_flow.rb:202:10:202:17 | ...[...] | hash_flow.rb:194:15:194:25 | call to taint : | hash_flow.rb:202:10:202:17 | ...[...] | $@ | hash_flow.rb:194:15:194:25 | call to taint : | call to taint : | +| hash_flow.rb:217:10:217:21 | call to dig | hash_flow.rb:210:15:210:25 | call to taint : | hash_flow.rb:217:10:217:21 | call to dig | $@ | hash_flow.rb:210:15:210:25 | call to taint : | call to taint : | +| hash_flow.rb:219:10:219:24 | call to dig | hash_flow.rb:213:19:213:29 | call to taint : | hash_flow.rb:219:10:219:24 | call to dig | $@ | hash_flow.rb:213:19:213:29 | call to taint : | call to taint : | +| hash_flow.rb:232:14:232:18 | value | hash_flow.rb:227:15:227:25 | call to taint : | hash_flow.rb:232:14:232:18 | value | $@ | hash_flow.rb:227:15:227:25 | call to taint : | call to taint : | +| hash_flow.rb:234:10:234:14 | ...[...] | hash_flow.rb:227:15:227:25 | call to taint : | hash_flow.rb:234:10:234:14 | ...[...] | $@ | hash_flow.rb:227:15:227:25 | call to taint : | call to taint : | +| hash_flow.rb:248:10:248:14 | ...[...] | hash_flow.rb:242:15:242:25 | call to taint : | hash_flow.rb:248:10:248:14 | ...[...] | $@ | hash_flow.rb:242:15:242:25 | call to taint : | call to taint : | +| hash_flow.rb:261:14:261:18 | value | hash_flow.rb:256:15:256:25 | call to taint : | hash_flow.rb:261:14:261:18 | value | $@ | hash_flow.rb:256:15:256:25 | call to taint : | call to taint : | +| hash_flow.rb:263:10:263:14 | ...[...] | hash_flow.rb:256:15:256:25 | call to taint : | hash_flow.rb:263:10:263:14 | ...[...] | $@ | hash_flow.rb:256:15:256:25 | call to taint : | call to taint : | +| hash_flow.rb:275:14:275:18 | value | hash_flow.rb:271:15:271:25 | call to taint : | hash_flow.rb:275:14:275:18 | value | $@ | hash_flow.rb:271:15:271:25 | call to taint : | call to taint : | +| hash_flow.rb:277:10:277:14 | ...[...] | hash_flow.rb:271:15:271:25 | call to taint : | hash_flow.rb:277:10:277:14 | ...[...] | $@ | hash_flow.rb:271:15:271:25 | call to taint : | call to taint : | +| hash_flow.rb:293:10:293:14 | ...[...] | hash_flow.rb:287:15:287:25 | call to taint : | hash_flow.rb:293:10:293:14 | ...[...] | $@ | hash_flow.rb:287:15:287:25 | call to taint : | call to taint : | +| hash_flow.rb:306:14:306:14 | x | hash_flow.rb:305:20:305:30 | call to taint : | hash_flow.rb:306:14:306:14 | x | $@ | hash_flow.rb:305:20:305:30 | call to taint : | call to taint : | +| hash_flow.rb:308:10:308:10 | b | hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:308:10:308:10 | b | $@ | hash_flow.rb:301:15:301:25 | call to taint : | call to taint : | +| hash_flow.rb:308:10:308:10 | b | hash_flow.rb:303:15:303:25 | call to taint : | hash_flow.rb:308:10:308:10 | b | $@ | hash_flow.rb:303:15:303:25 | call to taint : | call to taint : | +| hash_flow.rb:310:10:310:10 | b | hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:310:10:310:10 | b | $@ | hash_flow.rb:301:15:301:25 | call to taint : | call to taint : | +| hash_flow.rb:312:10:312:10 | b | hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:312:10:312:10 | b | $@ | hash_flow.rb:301:15:301:25 | call to taint : | call to taint : | +| hash_flow.rb:312:10:312:10 | b | hash_flow.rb:311:24:311:34 | call to taint : | hash_flow.rb:312:10:312:10 | b | $@ | hash_flow.rb:311:24:311:34 | call to taint : | call to taint : | +| hash_flow.rb:314:10:314:10 | b | hash_flow.rb:313:24:313:34 | call to taint : | hash_flow.rb:314:10:314:10 | b | $@ | hash_flow.rb:313:24:313:34 | call to taint : | call to taint : | +| hash_flow.rb:316:10:316:10 | b | hash_flow.rb:301:15:301:25 | call to taint : | hash_flow.rb:316:10:316:10 | b | $@ | hash_flow.rb:301:15:301:25 | call to taint : | call to taint : | +| hash_flow.rb:316:10:316:10 | b | hash_flow.rb:303:15:303:25 | call to taint : | hash_flow.rb:316:10:316:10 | b | $@ | hash_flow.rb:303:15:303:25 | call to taint : | call to taint : | +| hash_flow.rb:316:10:316:10 | b | hash_flow.rb:315:23:315:33 | call to taint : | hash_flow.rb:316:10:316:10 | b | $@ | hash_flow.rb:315:23:315:33 | call to taint : | call to taint : | +| hash_flow.rb:328:14:328:14 | x | hash_flow.rb:327:27:327:37 | call to taint : | hash_flow.rb:328:14:328:14 | x | $@ | hash_flow.rb:327:27:327:37 | call to taint : | call to taint : | +| hash_flow.rb:331:10:331:13 | ...[...] | hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:331:10:331:13 | ...[...] | $@ | hash_flow.rb:323:15:323:25 | call to taint : | call to taint : | +| hash_flow.rb:331:10:331:13 | ...[...] | hash_flow.rb:325:15:325:25 | call to taint : | hash_flow.rb:331:10:331:13 | ...[...] | $@ | hash_flow.rb:325:15:325:25 | call to taint : | call to taint : | +| hash_flow.rb:331:10:331:13 | ...[...] | hash_flow.rb:329:9:329:19 | call to taint : | hash_flow.rb:331:10:331:13 | ...[...] | $@ | hash_flow.rb:329:9:329:19 | call to taint : | call to taint : | +| hash_flow.rb:333:10:333:13 | ...[...] | hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:333:10:333:13 | ...[...] | $@ | hash_flow.rb:323:15:323:25 | call to taint : | call to taint : | +| hash_flow.rb:335:10:335:13 | ...[...] | hash_flow.rb:323:15:323:25 | call to taint : | hash_flow.rb:335:10:335:13 | ...[...] | $@ | hash_flow.rb:323:15:323:25 | call to taint : | call to taint : | +| hash_flow.rb:335:10:335:13 | ...[...] | hash_flow.rb:325:15:325:25 | call to taint : | hash_flow.rb:335:10:335:13 | ...[...] | $@ | hash_flow.rb:325:15:325:25 | call to taint : | call to taint : | +| hash_flow.rb:348:14:348:18 | value | hash_flow.rb:342:15:342:25 | call to taint : | hash_flow.rb:348:14:348:18 | value | $@ | hash_flow.rb:342:15:342:25 | call to taint : | call to taint : | +| hash_flow.rb:348:14:348:18 | value | hash_flow.rb:344:15:344:25 | call to taint : | hash_flow.rb:348:14:348:18 | value | $@ | hash_flow.rb:344:15:344:25 | call to taint : | call to taint : | +| hash_flow.rb:351:10:351:16 | ( ... ) | hash_flow.rb:342:15:342:25 | call to taint : | hash_flow.rb:351:10:351:16 | ( ... ) | $@ | hash_flow.rb:342:15:342:25 | call to taint : | call to taint : | +| hash_flow.rb:364:14:364:18 | value | hash_flow.rb:358:15:358:25 | call to taint : | hash_flow.rb:364:14:364:18 | value | $@ | hash_flow.rb:358:15:358:25 | call to taint : | call to taint : | +| hash_flow.rb:364:14:364:18 | value | hash_flow.rb:360:15:360:25 | call to taint : | hash_flow.rb:364:14:364:18 | value | $@ | hash_flow.rb:360:15:360:25 | call to taint : | call to taint : | +| hash_flow.rb:367:10:367:19 | ( ... ) | hash_flow.rb:358:15:358:25 | call to taint : | hash_flow.rb:367:10:367:19 | ( ... ) | $@ | hash_flow.rb:358:15:358:25 | call to taint : | call to taint : | +| hash_flow.rb:379:10:379:15 | ( ... ) | hash_flow.rb:374:15:374:25 | call to taint : | hash_flow.rb:379:10:379:15 | ( ... ) | $@ | hash_flow.rb:374:15:374:25 | call to taint : | call to taint : | +| hash_flow.rb:379:10:379:15 | ( ... ) | hash_flow.rb:376:15:376:25 | call to taint : | hash_flow.rb:379:10:379:15 | ( ... ) | $@ | hash_flow.rb:376:15:376:25 | call to taint : | call to taint : | +| hash_flow.rb:392:14:392:18 | value | hash_flow.rb:386:15:386:25 | call to taint : | hash_flow.rb:392:14:392:18 | value | $@ | hash_flow.rb:386:15:386:25 | call to taint : | call to taint : | +| hash_flow.rb:392:14:392:18 | value | hash_flow.rb:388:15:388:25 | call to taint : | hash_flow.rb:392:14:392:18 | value | $@ | hash_flow.rb:388:15:388:25 | call to taint : | call to taint : | +| hash_flow.rb:395:10:395:19 | ( ... ) | hash_flow.rb:386:15:386:25 | call to taint : | hash_flow.rb:395:10:395:19 | ( ... ) | $@ | hash_flow.rb:386:15:386:25 | call to taint : | call to taint : | +| hash_flow.rb:396:10:396:16 | ( ... ) | hash_flow.rb:386:15:386:25 | call to taint : | hash_flow.rb:396:10:396:16 | ( ... ) | $@ | hash_flow.rb:386:15:386:25 | call to taint : | call to taint : | +| hash_flow.rb:414:14:414:22 | old_value | hash_flow.rb:403:15:403:25 | call to taint : | hash_flow.rb:414:14:414:22 | old_value | $@ | hash_flow.rb:403:15:403:25 | call to taint : | call to taint : | +| hash_flow.rb:414:14:414:22 | old_value | hash_flow.rb:405:15:405:25 | call to taint : | hash_flow.rb:414:14:414:22 | old_value | $@ | hash_flow.rb:405:15:405:25 | call to taint : | call to taint : | +| hash_flow.rb:414:14:414:22 | old_value | hash_flow.rb:408:15:408:25 | call to taint : | hash_flow.rb:414:14:414:22 | old_value | $@ | hash_flow.rb:408:15:408:25 | call to taint : | call to taint : | +| hash_flow.rb:414:14:414:22 | old_value | hash_flow.rb:410:15:410:25 | call to taint : | hash_flow.rb:414:14:414:22 | old_value | $@ | hash_flow.rb:410:15:410:25 | call to taint : | call to taint : | +| hash_flow.rb:415:14:415:22 | new_value | hash_flow.rb:403:15:403:25 | call to taint : | hash_flow.rb:415:14:415:22 | new_value | $@ | hash_flow.rb:403:15:403:25 | call to taint : | call to taint : | +| hash_flow.rb:415:14:415:22 | new_value | hash_flow.rb:405:15:405:25 | call to taint : | hash_flow.rb:415:14:415:22 | new_value | $@ | hash_flow.rb:405:15:405:25 | call to taint : | call to taint : | +| hash_flow.rb:415:14:415:22 | new_value | hash_flow.rb:408:15:408:25 | call to taint : | hash_flow.rb:415:14:415:22 | new_value | $@ | hash_flow.rb:408:15:408:25 | call to taint : | call to taint : | +| hash_flow.rb:415:14:415:22 | new_value | hash_flow.rb:410:15:410:25 | call to taint : | hash_flow.rb:415:14:415:22 | new_value | $@ | hash_flow.rb:410:15:410:25 | call to taint : | call to taint : | +| hash_flow.rb:417:10:417:19 | ( ... ) | hash_flow.rb:403:15:403:25 | call to taint : | hash_flow.rb:417:10:417:19 | ( ... ) | $@ | hash_flow.rb:403:15:403:25 | call to taint : | call to taint : | +| hash_flow.rb:419:10:419:19 | ( ... ) | hash_flow.rb:405:15:405:25 | call to taint : | hash_flow.rb:419:10:419:19 | ( ... ) | $@ | hash_flow.rb:405:15:405:25 | call to taint : | call to taint : | +| hash_flow.rb:420:10:420:19 | ( ... ) | hash_flow.rb:408:15:408:25 | call to taint : | hash_flow.rb:420:10:420:19 | ( ... ) | $@ | hash_flow.rb:408:15:408:25 | call to taint : | call to taint : | +| hash_flow.rb:422:10:422:19 | ( ... ) | hash_flow.rb:410:15:410:25 | call to taint : | hash_flow.rb:422:10:422:19 | ( ... ) | $@ | hash_flow.rb:410:15:410:25 | call to taint : | call to taint : | +| hash_flow.rb:440:14:440:22 | old_value | hash_flow.rb:429:15:429:25 | call to taint : | hash_flow.rb:440:14:440:22 | old_value | $@ | hash_flow.rb:429:15:429:25 | call to taint : | call to taint : | +| hash_flow.rb:440:14:440:22 | old_value | hash_flow.rb:431:15:431:25 | call to taint : | hash_flow.rb:440:14:440:22 | old_value | $@ | hash_flow.rb:431:15:431:25 | call to taint : | call to taint : | +| hash_flow.rb:440:14:440:22 | old_value | hash_flow.rb:434:15:434:25 | call to taint : | hash_flow.rb:440:14:440:22 | old_value | $@ | hash_flow.rb:434:15:434:25 | call to taint : | call to taint : | +| hash_flow.rb:440:14:440:22 | old_value | hash_flow.rb:436:15:436:25 | call to taint : | hash_flow.rb:440:14:440:22 | old_value | $@ | hash_flow.rb:436:15:436:25 | call to taint : | call to taint : | +| hash_flow.rb:441:14:441:22 | new_value | hash_flow.rb:429:15:429:25 | call to taint : | hash_flow.rb:441:14:441:22 | new_value | $@ | hash_flow.rb:429:15:429:25 | call to taint : | call to taint : | +| hash_flow.rb:441:14:441:22 | new_value | hash_flow.rb:431:15:431:25 | call to taint : | hash_flow.rb:441:14:441:22 | new_value | $@ | hash_flow.rb:431:15:431:25 | call to taint : | call to taint : | +| hash_flow.rb:441:14:441:22 | new_value | hash_flow.rb:434:15:434:25 | call to taint : | hash_flow.rb:441:14:441:22 | new_value | $@ | hash_flow.rb:434:15:434:25 | call to taint : | call to taint : | +| hash_flow.rb:441:14:441:22 | new_value | hash_flow.rb:436:15:436:25 | call to taint : | hash_flow.rb:441:14:441:22 | new_value | $@ | hash_flow.rb:436:15:436:25 | call to taint : | call to taint : | +| hash_flow.rb:443:10:443:19 | ( ... ) | hash_flow.rb:429:15:429:25 | call to taint : | hash_flow.rb:443:10:443:19 | ( ... ) | $@ | hash_flow.rb:429:15:429:25 | call to taint : | call to taint : | +| hash_flow.rb:445:10:445:19 | ( ... ) | hash_flow.rb:431:15:431:25 | call to taint : | hash_flow.rb:445:10:445:19 | ( ... ) | $@ | hash_flow.rb:431:15:431:25 | call to taint : | call to taint : | +| hash_flow.rb:446:10:446:19 | ( ... ) | hash_flow.rb:434:15:434:25 | call to taint : | hash_flow.rb:446:10:446:19 | ( ... ) | $@ | hash_flow.rb:434:15:434:25 | call to taint : | call to taint : | +| hash_flow.rb:448:10:448:19 | ( ... ) | hash_flow.rb:436:15:436:25 | call to taint : | hash_flow.rb:448:10:448:19 | ( ... ) | $@ | hash_flow.rb:436:15:436:25 | call to taint : | call to taint : | +| hash_flow.rb:450:10:450:20 | ( ... ) | hash_flow.rb:429:15:429:25 | call to taint : | hash_flow.rb:450:10:450:20 | ( ... ) | $@ | hash_flow.rb:429:15:429:25 | call to taint : | call to taint : | +| hash_flow.rb:452:10:452:20 | ( ... ) | hash_flow.rb:431:15:431:25 | call to taint : | hash_flow.rb:452:10:452:20 | ( ... ) | $@ | hash_flow.rb:431:15:431:25 | call to taint : | call to taint : | +| hash_flow.rb:453:10:453:20 | ( ... ) | hash_flow.rb:434:15:434:25 | call to taint : | hash_flow.rb:453:10:453:20 | ( ... ) | $@ | hash_flow.rb:434:15:434:25 | call to taint : | call to taint : | +| hash_flow.rb:455:10:455:20 | ( ... ) | hash_flow.rb:436:15:436:25 | call to taint : | hash_flow.rb:455:10:455:20 | ( ... ) | $@ | hash_flow.rb:436:15:436:25 | call to taint : | call to taint : | +| hash_flow.rb:467:10:467:13 | ...[...] | hash_flow.rb:462:15:462:25 | call to taint : | hash_flow.rb:467:10:467:13 | ...[...] | $@ | hash_flow.rb:462:15:462:25 | call to taint : | call to taint : | +| hash_flow.rb:479:14:479:18 | value | hash_flow.rb:474:15:474:25 | call to taint : | hash_flow.rb:479:14:479:18 | value | $@ | hash_flow.rb:474:15:474:25 | call to taint : | call to taint : | +| hash_flow.rb:482:10:482:14 | ...[...] | hash_flow.rb:474:15:474:25 | call to taint : | hash_flow.rb:482:10:482:14 | ...[...] | $@ | hash_flow.rb:474:15:474:25 | call to taint : | call to taint : | +| hash_flow.rb:494:14:494:18 | value | hash_flow.rb:489:15:489:25 | call to taint : | hash_flow.rb:494:14:494:18 | value | $@ | hash_flow.rb:489:15:489:25 | call to taint : | call to taint : | +| hash_flow.rb:497:10:497:14 | ...[...] | hash_flow.rb:489:15:489:25 | call to taint : | hash_flow.rb:497:10:497:14 | ...[...] | $@ | hash_flow.rb:489:15:489:25 | call to taint : | call to taint : | +| hash_flow.rb:498:10:498:17 | ...[...] | hash_flow.rb:489:15:489:25 | call to taint : | hash_flow.rb:498:10:498:17 | ...[...] | $@ | hash_flow.rb:489:15:489:25 | call to taint : | call to taint : | +| hash_flow.rb:513:10:513:20 | ( ... ) | hash_flow.rb:505:15:505:25 | call to taint : | hash_flow.rb:513:10:513:20 | ( ... ) | $@ | hash_flow.rb:505:15:505:25 | call to taint : | call to taint : | +| hash_flow.rb:515:10:515:20 | ( ... ) | hash_flow.rb:507:15:507:25 | call to taint : | hash_flow.rb:515:10:515:20 | ( ... ) | $@ | hash_flow.rb:507:15:507:25 | call to taint : | call to taint : | +| hash_flow.rb:526:14:526:18 | value | hash_flow.rb:520:15:520:25 | call to taint : | hash_flow.rb:526:14:526:18 | value | $@ | hash_flow.rb:520:15:520:25 | call to taint : | call to taint : | +| hash_flow.rb:526:14:526:18 | value | hash_flow.rb:522:15:522:25 | call to taint : | hash_flow.rb:526:14:526:18 | value | $@ | hash_flow.rb:522:15:522:25 | call to taint : | call to taint : | +| hash_flow.rb:529:10:529:16 | ( ... ) | hash_flow.rb:520:15:520:25 | call to taint : | hash_flow.rb:529:10:529:16 | ( ... ) | $@ | hash_flow.rb:520:15:520:25 | call to taint : | call to taint : | +| hash_flow.rb:542:14:542:18 | value | hash_flow.rb:536:15:536:25 | call to taint : | hash_flow.rb:542:14:542:18 | value | $@ | hash_flow.rb:536:15:536:25 | call to taint : | call to taint : | +| hash_flow.rb:542:14:542:18 | value | hash_flow.rb:538:15:538:25 | call to taint : | hash_flow.rb:542:14:542:18 | value | $@ | hash_flow.rb:538:15:538:25 | call to taint : | call to taint : | +| hash_flow.rb:545:10:545:19 | ( ... ) | hash_flow.rb:536:15:536:25 | call to taint : | hash_flow.rb:545:10:545:19 | ( ... ) | $@ | hash_flow.rb:536:15:536:25 | call to taint : | call to taint : | +| hash_flow.rb:557:10:557:19 | ( ... ) | hash_flow.rb:552:15:552:25 | call to taint : | hash_flow.rb:557:10:557:19 | ( ... ) | $@ | hash_flow.rb:552:15:552:25 | call to taint : | call to taint : | +| hash_flow.rb:559:10:559:15 | ( ... ) | hash_flow.rb:552:15:552:25 | call to taint : | hash_flow.rb:559:10:559:15 | ( ... ) | $@ | hash_flow.rb:552:15:552:25 | call to taint : | call to taint : | +| hash_flow.rb:559:10:559:15 | ( ... ) | hash_flow.rb:554:15:554:25 | call to taint : | hash_flow.rb:559:10:559:15 | ( ... ) | $@ | hash_flow.rb:554:15:554:25 | call to taint : | call to taint : | +| hash_flow.rb:571:10:571:16 | ( ... ) | hash_flow.rb:566:15:566:25 | call to taint : | hash_flow.rb:571:10:571:16 | ( ... ) | $@ | hash_flow.rb:566:15:566:25 | call to taint : | call to taint : | +| hash_flow.rb:576:10:576:16 | ( ... ) | hash_flow.rb:566:15:566:25 | call to taint : | hash_flow.rb:576:10:576:16 | ( ... ) | $@ | hash_flow.rb:566:15:566:25 | call to taint : | call to taint : | +| hash_flow.rb:578:10:578:16 | ( ... ) | hash_flow.rb:568:15:568:25 | call to taint : | hash_flow.rb:578:10:578:16 | ( ... ) | $@ | hash_flow.rb:568:15:568:25 | call to taint : | call to taint : | +| hash_flow.rb:591:10:591:18 | ( ... ) | hash_flow.rb:585:15:585:25 | call to taint : | hash_flow.rb:591:10:591:18 | ( ... ) | $@ | hash_flow.rb:585:15:585:25 | call to taint : | call to taint : | +| hash_flow.rb:591:10:591:18 | ( ... ) | hash_flow.rb:587:15:587:25 | call to taint : | hash_flow.rb:591:10:591:18 | ( ... ) | $@ | hash_flow.rb:587:15:587:25 | call to taint : | call to taint : | +| hash_flow.rb:603:10:603:16 | ( ... ) | hash_flow.rb:598:15:598:25 | call to taint : | hash_flow.rb:603:10:603:16 | ( ... ) | $@ | hash_flow.rb:598:15:598:25 | call to taint : | call to taint : | +| hash_flow.rb:605:10:605:16 | ( ... ) | hash_flow.rb:600:15:600:25 | call to taint : | hash_flow.rb:605:10:605:16 | ( ... ) | $@ | hash_flow.rb:600:15:600:25 | call to taint : | call to taint : | +| hash_flow.rb:609:14:609:18 | value | hash_flow.rb:598:15:598:25 | call to taint : | hash_flow.rb:609:14:609:18 | value | $@ | hash_flow.rb:598:15:598:25 | call to taint : | call to taint : | +| hash_flow.rb:609:14:609:18 | value | hash_flow.rb:600:15:600:25 | call to taint : | hash_flow.rb:609:14:609:18 | value | $@ | hash_flow.rb:600:15:600:25 | call to taint : | call to taint : | +| hash_flow.rb:612:10:612:16 | ( ... ) | hash_flow.rb:610:14:610:24 | call to taint : | hash_flow.rb:612:10:612:16 | ( ... ) | $@ | hash_flow.rb:610:14:610:24 | call to taint : | call to taint : | +| hash_flow.rb:624:10:624:17 | ( ... ) | hash_flow.rb:619:15:619:25 | call to taint : | hash_flow.rb:624:10:624:17 | ( ... ) | $@ | hash_flow.rb:619:15:619:25 | call to taint : | call to taint : | +| hash_flow.rb:624:10:624:17 | ( ... ) | hash_flow.rb:621:15:621:25 | call to taint : | hash_flow.rb:624:10:624:17 | ( ... ) | $@ | hash_flow.rb:621:15:621:25 | call to taint : | call to taint : | +| hash_flow.rb:625:10:625:17 | ( ... ) | hash_flow.rb:619:15:619:25 | call to taint : | hash_flow.rb:625:10:625:17 | ( ... ) | $@ | hash_flow.rb:619:15:619:25 | call to taint : | call to taint : | +| hash_flow.rb:625:10:625:17 | ( ... ) | hash_flow.rb:621:15:621:25 | call to taint : | hash_flow.rb:625:10:625:17 | ( ... ) | $@ | hash_flow.rb:621:15:621:25 | call to taint : | call to taint : | +| hash_flow.rb:626:10:626:17 | ( ... ) | hash_flow.rb:619:15:619:25 | call to taint : | hash_flow.rb:626:10:626:17 | ( ... ) | $@ | hash_flow.rb:619:15:619:25 | call to taint : | call to taint : | +| hash_flow.rb:626:10:626:17 | ( ... ) | hash_flow.rb:621:15:621:25 | call to taint : | hash_flow.rb:626:10:626:17 | ( ... ) | $@ | hash_flow.rb:621:15:621:25 | call to taint : | call to taint : | +| hash_flow.rb:638:10:638:20 | ( ... ) | hash_flow.rb:633:15:633:25 | call to taint : | hash_flow.rb:638:10:638:20 | ( ... ) | $@ | hash_flow.rb:633:15:633:25 | call to taint : | call to taint : | +| hash_flow.rb:638:10:638:20 | ( ... ) | hash_flow.rb:635:15:635:25 | call to taint : | hash_flow.rb:638:10:638:20 | ( ... ) | $@ | hash_flow.rb:635:15:635:25 | call to taint : | call to taint : | +| hash_flow.rb:639:10:639:20 | ( ... ) | hash_flow.rb:633:15:633:25 | call to taint : | hash_flow.rb:639:10:639:20 | ( ... ) | $@ | hash_flow.rb:633:15:633:25 | call to taint : | call to taint : | +| hash_flow.rb:639:10:639:20 | ( ... ) | hash_flow.rb:635:15:635:25 | call to taint : | hash_flow.rb:639:10:639:20 | ( ... ) | $@ | hash_flow.rb:635:15:635:25 | call to taint : | call to taint : | +| hash_flow.rb:640:10:640:20 | ( ... ) | hash_flow.rb:633:15:633:25 | call to taint : | hash_flow.rb:640:10:640:20 | ( ... ) | $@ | hash_flow.rb:633:15:633:25 | call to taint : | call to taint : | +| hash_flow.rb:640:10:640:20 | ( ... ) | hash_flow.rb:635:15:635:25 | call to taint : | hash_flow.rb:640:10:640:20 | ( ... ) | $@ | hash_flow.rb:635:15:635:25 | call to taint : | call to taint : | +| hash_flow.rb:652:14:652:18 | value | hash_flow.rb:647:15:647:25 | call to taint : | hash_flow.rb:652:14:652:18 | value | $@ | hash_flow.rb:647:15:647:25 | call to taint : | call to taint : | +| hash_flow.rb:652:14:652:18 | value | hash_flow.rb:649:15:649:25 | call to taint : | hash_flow.rb:652:14:652:18 | value | $@ | hash_flow.rb:649:15:649:25 | call to taint : | call to taint : | +| hash_flow.rb:655:10:655:19 | ( ... ) | hash_flow.rb:647:15:647:25 | call to taint : | hash_flow.rb:655:10:655:19 | ( ... ) | $@ | hash_flow.rb:647:15:647:25 | call to taint : | call to taint : | +| hash_flow.rb:656:10:656:16 | ( ... ) | hash_flow.rb:653:9:653:19 | call to taint : | hash_flow.rb:656:10:656:16 | ( ... ) | $@ | hash_flow.rb:653:9:653:19 | call to taint : | call to taint : | +| hash_flow.rb:668:14:668:18 | value | hash_flow.rb:663:15:663:25 | call to taint : | hash_flow.rb:668:14:668:18 | value | $@ | hash_flow.rb:663:15:663:25 | call to taint : | call to taint : | +| hash_flow.rb:668:14:668:18 | value | hash_flow.rb:665:15:665:25 | call to taint : | hash_flow.rb:668:14:668:18 | value | $@ | hash_flow.rb:665:15:665:25 | call to taint : | call to taint : | +| hash_flow.rb:671:10:671:19 | ( ... ) | hash_flow.rb:669:9:669:19 | call to taint : | hash_flow.rb:671:10:671:19 | ( ... ) | $@ | hash_flow.rb:669:9:669:19 | call to taint : | call to taint : | +| hash_flow.rb:689:14:689:22 | old_value | hash_flow.rb:678:15:678:25 | call to taint : | hash_flow.rb:689:14:689:22 | old_value | $@ | hash_flow.rb:678:15:678:25 | call to taint : | call to taint : | +| hash_flow.rb:689:14:689:22 | old_value | hash_flow.rb:680:15:680:25 | call to taint : | hash_flow.rb:689:14:689:22 | old_value | $@ | hash_flow.rb:680:15:680:25 | call to taint : | call to taint : | +| hash_flow.rb:689:14:689:22 | old_value | hash_flow.rb:683:15:683:25 | call to taint : | hash_flow.rb:689:14:689:22 | old_value | $@ | hash_flow.rb:683:15:683:25 | call to taint : | call to taint : | +| hash_flow.rb:689:14:689:22 | old_value | hash_flow.rb:685:15:685:25 | call to taint : | hash_flow.rb:689:14:689:22 | old_value | $@ | hash_flow.rb:685:15:685:25 | call to taint : | call to taint : | +| hash_flow.rb:690:14:690:22 | new_value | hash_flow.rb:678:15:678:25 | call to taint : | hash_flow.rb:690:14:690:22 | new_value | $@ | hash_flow.rb:678:15:678:25 | call to taint : | call to taint : | +| hash_flow.rb:690:14:690:22 | new_value | hash_flow.rb:680:15:680:25 | call to taint : | hash_flow.rb:690:14:690:22 | new_value | $@ | hash_flow.rb:680:15:680:25 | call to taint : | call to taint : | +| hash_flow.rb:690:14:690:22 | new_value | hash_flow.rb:683:15:683:25 | call to taint : | hash_flow.rb:690:14:690:22 | new_value | $@ | hash_flow.rb:683:15:683:25 | call to taint : | call to taint : | +| hash_flow.rb:690:14:690:22 | new_value | hash_flow.rb:685:15:685:25 | call to taint : | hash_flow.rb:690:14:690:22 | new_value | $@ | hash_flow.rb:685:15:685:25 | call to taint : | call to taint : | +| hash_flow.rb:692:10:692:19 | ( ... ) | hash_flow.rb:678:15:678:25 | call to taint : | hash_flow.rb:692:10:692:19 | ( ... ) | $@ | hash_flow.rb:678:15:678:25 | call to taint : | call to taint : | +| hash_flow.rb:694:10:694:19 | ( ... ) | hash_flow.rb:680:15:680:25 | call to taint : | hash_flow.rb:694:10:694:19 | ( ... ) | $@ | hash_flow.rb:680:15:680:25 | call to taint : | call to taint : | +| hash_flow.rb:695:10:695:19 | ( ... ) | hash_flow.rb:683:15:683:25 | call to taint : | hash_flow.rb:695:10:695:19 | ( ... ) | $@ | hash_flow.rb:683:15:683:25 | call to taint : | call to taint : | +| hash_flow.rb:697:10:697:19 | ( ... ) | hash_flow.rb:685:15:685:25 | call to taint : | hash_flow.rb:697:10:697:19 | ( ... ) | $@ | hash_flow.rb:685:15:685:25 | call to taint : | call to taint : | +| hash_flow.rb:699:10:699:20 | ( ... ) | hash_flow.rb:678:15:678:25 | call to taint : | hash_flow.rb:699:10:699:20 | ( ... ) | $@ | hash_flow.rb:678:15:678:25 | call to taint : | call to taint : | +| hash_flow.rb:701:10:701:20 | ( ... ) | hash_flow.rb:680:15:680:25 | call to taint : | hash_flow.rb:701:10:701:20 | ( ... ) | $@ | hash_flow.rb:680:15:680:25 | call to taint : | call to taint : | +| hash_flow.rb:702:10:702:20 | ( ... ) | hash_flow.rb:683:15:683:25 | call to taint : | hash_flow.rb:702:10:702:20 | ( ... ) | $@ | hash_flow.rb:683:15:683:25 | call to taint : | call to taint : | +| hash_flow.rb:704:10:704:20 | ( ... ) | hash_flow.rb:685:15:685:25 | call to taint : | hash_flow.rb:704:10:704:20 | ( ... ) | $@ | hash_flow.rb:685:15:685:25 | call to taint : | call to taint : | +| hash_flow.rb:716:10:716:15 | ( ... ) | hash_flow.rb:711:15:711:25 | call to taint : | hash_flow.rb:716:10:716:15 | ( ... ) | $@ | hash_flow.rb:711:15:711:25 | call to taint : | call to taint : | +| hash_flow.rb:716:10:716:15 | ( ... ) | hash_flow.rb:713:15:713:25 | call to taint : | hash_flow.rb:716:10:716:15 | ( ... ) | $@ | hash_flow.rb:713:15:713:25 | call to taint : | call to taint : | +| hash_flow.rb:728:10:728:13 | ...[...] | hash_flow.rb:723:15:723:25 | call to taint : | hash_flow.rb:728:10:728:13 | ...[...] | $@ | hash_flow.rb:723:15:723:25 | call to taint : | call to taint : | +| hash_flow.rb:730:10:730:13 | ...[...] | hash_flow.rb:723:15:723:25 | call to taint : | hash_flow.rb:730:10:730:13 | ...[...] | $@ | hash_flow.rb:723:15:723:25 | call to taint : | call to taint : | +| hash_flow.rb:730:10:730:13 | ...[...] | hash_flow.rb:725:15:725:25 | call to taint : | hash_flow.rb:730:10:730:13 | ...[...] | $@ | hash_flow.rb:725:15:725:25 | call to taint : | call to taint : | +| hash_flow.rb:747:10:747:17 | ...[...] | hash_flow.rb:737:15:737:25 | call to taint : | hash_flow.rb:747:10:747:17 | ...[...] | $@ | hash_flow.rb:737:15:737:25 | call to taint : | call to taint : | +| hash_flow.rb:749:10:749:17 | ...[...] | hash_flow.rb:739:15:739:25 | call to taint : | hash_flow.rb:749:10:749:17 | ...[...] | $@ | hash_flow.rb:739:15:739:25 | call to taint : | call to taint : | +| hash_flow.rb:750:10:750:17 | ...[...] | hash_flow.rb:742:15:742:25 | call to taint : | hash_flow.rb:750:10:750:17 | ...[...] | $@ | hash_flow.rb:742:15:742:25 | call to taint : | call to taint : | +| hash_flow.rb:752:10:752:17 | ...[...] | hash_flow.rb:744:15:744:25 | call to taint : | hash_flow.rb:752:10:752:17 | ...[...] | $@ | hash_flow.rb:744:15:744:25 | call to taint : | call to taint : | +| hash_flow.rb:753:10:753:17 | ...[...] | hash_flow.rb:746:29:746:39 | call to taint : | hash_flow.rb:753:10:753:17 | ...[...] | $@ | hash_flow.rb:746:29:746:39 | call to taint : | call to taint : | diff --git a/ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb b/ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb index f9de83dd47c..6802475b89b 100644 --- a/ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb +++ b/ruby/ql/test/library-tests/dataflow/hash-flow/hash_flow.rb @@ -68,6 +68,14 @@ def m3() hash4 = Hash[:a, taint(3.4), :b, 1] sink(hash4[:a]) # $ hasValueFlow=3.4 sink(hash4[:b]) + + hash5 = Hash["a" => taint(3.5), "b" => 1] + sink(hash5["a"]) # $ hasValueFlow=3.5 + sink(hash5["b"]) + + hash6 = Hash[{"a" => taint(3.6), "b" => 1}] + sink(hash6["a"]) # $ hasValueFlow=3.6 + sink(hash6["b"]) end m3() diff --git a/ruby/ql/test/library-tests/dataflow/params/params-flow.expected b/ruby/ql/test/library-tests/dataflow/params/params-flow.expected index 3df4f046a07..82840bcf95e 100644 --- a/ruby/ql/test/library-tests/dataflow/params/params-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/params/params-flow.expected @@ -26,6 +26,16 @@ edges | params_flow.rb:35:12:35:20 | call to taint : | params_flow.rb:25:12:25:13 | p1 : | | params_flow.rb:35:23:35:28 | ** ... [element :p3] : | params_flow.rb:25:17:25:24 | **kwargs [element :p3] : | | params_flow.rb:35:25:35:28 | args [element :p3] : | params_flow.rb:35:23:35:28 | ** ... [element :p3] : | +| params_flow.rb:37:16:37:24 | call to taint : | params_flow.rb:38:10:38:13 | args [element :p1] : | +| params_flow.rb:37:34:37:42 | call to taint : | params_flow.rb:38:10:38:13 | args [element :p2] : | +| params_flow.rb:38:8:38:13 | ** ... [element :p1] : | params_flow.rb:25:12:25:13 | p1 : | +| params_flow.rb:38:8:38:13 | ** ... [element :p2] : | params_flow.rb:25:17:25:24 | **kwargs [element :p2] : | +| params_flow.rb:38:10:38:13 | args [element :p1] : | params_flow.rb:38:8:38:13 | ** ... [element :p1] : | +| params_flow.rb:38:10:38:13 | args [element :p2] : | params_flow.rb:38:8:38:13 | ** ... [element :p2] : | +| params_flow.rb:40:16:40:24 | call to taint : | params_flow.rb:41:26:41:29 | args [element :p1] : | +| params_flow.rb:41:13:41:21 | call to taint : | params_flow.rb:16:18:16:19 | p2 : | +| params_flow.rb:41:24:41:29 | ** ... [element :p1] : | params_flow.rb:16:13:16:14 | p1 : | +| params_flow.rb:41:26:41:29 | args [element :p1] : | params_flow.rb:41:24:41:29 | ** ... [element :p1] : | nodes | params_flow.rb:9:16:9:17 | p1 : | semmle.label | p1 : | | params_flow.rb:9:20:9:21 | p2 : | semmle.label | p2 : | @@ -60,6 +70,16 @@ nodes | params_flow.rb:35:12:35:20 | call to taint : | semmle.label | call to taint : | | params_flow.rb:35:23:35:28 | ** ... [element :p3] : | semmle.label | ** ... [element :p3] : | | params_flow.rb:35:25:35:28 | args [element :p3] : | semmle.label | args [element :p3] : | +| params_flow.rb:37:16:37:24 | call to taint : | semmle.label | call to taint : | +| params_flow.rb:37:34:37:42 | call to taint : | semmle.label | call to taint : | +| params_flow.rb:38:8:38:13 | ** ... [element :p1] : | semmle.label | ** ... [element :p1] : | +| params_flow.rb:38:8:38:13 | ** ... [element :p2] : | semmle.label | ** ... [element :p2] : | +| params_flow.rb:38:10:38:13 | args [element :p1] : | semmle.label | args [element :p1] : | +| params_flow.rb:38:10:38:13 | args [element :p2] : | semmle.label | args [element :p2] : | +| params_flow.rb:40:16:40:24 | call to taint : | semmle.label | call to taint : | +| params_flow.rb:41:13:41:21 | call to taint : | semmle.label | call to taint : | +| params_flow.rb:41:24:41:29 | ** ... [element :p1] : | semmle.label | ** ... [element :p1] : | +| params_flow.rb:41:26:41:29 | args [element :p1] : | semmle.label | args [element :p1] : | subpaths #select | params_flow.rb:10:10:10:11 | p1 | params_flow.rb:14:12:14:19 | call to taint : | params_flow.rb:10:10:10:11 | p1 | $@ | params_flow.rb:14:12:14:19 | call to taint : | call to taint : | @@ -67,11 +87,15 @@ subpaths | params_flow.rb:17:10:17:11 | p1 | params_flow.rb:21:13:21:20 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:21:13:21:20 | call to taint : | call to taint : | | params_flow.rb:17:10:17:11 | p1 | params_flow.rb:22:27:22:34 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:22:27:22:34 | call to taint : | call to taint : | | params_flow.rb:17:10:17:11 | p1 | params_flow.rb:23:33:23:40 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:23:33:23:40 | call to taint : | call to taint : | +| params_flow.rb:17:10:17:11 | p1 | params_flow.rb:40:16:40:24 | call to taint : | params_flow.rb:17:10:17:11 | p1 | $@ | params_flow.rb:40:16:40:24 | call to taint : | call to taint : | | params_flow.rb:18:10:18:11 | p2 | params_flow.rb:21:27:21:34 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:21:27:21:34 | call to taint : | call to taint : | | params_flow.rb:18:10:18:11 | p2 | params_flow.rb:22:13:22:20 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:22:13:22:20 | call to taint : | call to taint : | | params_flow.rb:18:10:18:11 | p2 | params_flow.rb:23:16:23:23 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:23:16:23:23 | call to taint : | call to taint : | +| params_flow.rb:18:10:18:11 | p2 | params_flow.rb:41:13:41:21 | call to taint : | params_flow.rb:18:10:18:11 | p2 | $@ | params_flow.rb:41:13:41:21 | call to taint : | call to taint : | | params_flow.rb:26:10:26:11 | p1 | params_flow.rb:33:12:33:19 | call to taint : | params_flow.rb:26:10:26:11 | p1 | $@ | params_flow.rb:33:12:33:19 | call to taint : | call to taint : | | params_flow.rb:26:10:26:11 | p1 | params_flow.rb:35:12:35:20 | call to taint : | params_flow.rb:26:10:26:11 | p1 | $@ | params_flow.rb:35:12:35:20 | call to taint : | call to taint : | +| params_flow.rb:26:10:26:11 | p1 | params_flow.rb:37:16:37:24 | call to taint : | params_flow.rb:26:10:26:11 | p1 | $@ | params_flow.rb:37:16:37:24 | call to taint : | call to taint : | | params_flow.rb:28:10:28:22 | ( ... ) | params_flow.rb:33:26:33:34 | call to taint : | params_flow.rb:28:10:28:22 | ( ... ) | $@ | params_flow.rb:33:26:33:34 | call to taint : | call to taint : | +| params_flow.rb:28:10:28:22 | ( ... ) | params_flow.rb:37:34:37:42 | call to taint : | params_flow.rb:28:10:28:22 | ( ... ) | $@ | params_flow.rb:37:34:37:42 | call to taint : | call to taint : | | params_flow.rb:29:10:29:22 | ( ... ) | params_flow.rb:33:41:33:49 | call to taint : | params_flow.rb:29:10:29:22 | ( ... ) | $@ | params_flow.rb:33:41:33:49 | call to taint : | call to taint : | | params_flow.rb:29:10:29:22 | ( ... ) | params_flow.rb:34:14:34:22 | call to taint : | params_flow.rb:29:10:29:22 | ( ... ) | $@ | params_flow.rb:34:14:34:22 | call to taint : | call to taint : | diff --git a/ruby/ql/test/library-tests/dataflow/params/params_flow.rb b/ruby/ql/test/library-tests/dataflow/params/params_flow.rb index d3a99e61f37..a41706261e2 100644 --- a/ruby/ql/test/library-tests/dataflow/params/params_flow.rb +++ b/ruby/ql/test/library-tests/dataflow/params/params_flow.rb @@ -14,8 +14,8 @@ end positional(taint(1), taint(2)) def keyword(p1:, p2:) - sink p1 # $ hasValueFlow=3 $ hasValueFlow=6 $ hasValueFlow=8 - sink p2 # $ hasValueFlow=4 $ hasValueFlow=5 $ hasValueFlow=7 + sink p1 # $ hasValueFlow=3 $ hasValueFlow=6 $ hasValueFlow=8 $ hasValueFlow=16 + sink p2 # $ hasValueFlow=4 $ hasValueFlow=5 $ hasValueFlow=7 $ hasValueFlow=17 end keyword(p1: taint(3), p2: taint(4)) @@ -23,9 +23,9 @@ keyword(p2: taint(5), p1: taint(6)) keyword(:p2 => taint(7), :p1 => taint(8)) def kwargs(p1:, **kwargs) - sink p1 # $ hasValueFlow=9 $ hasValueFlow=13 + sink p1 # $ hasValueFlow=9 $ hasValueFlow=13 $ hasValueFlow=14 sink (kwargs[:p1]) - sink (kwargs[:p2]) # $ hasValueFlow=10 + sink (kwargs[:p2]) # $ hasValueFlow=10 $ hasValueFlow=15 sink (kwargs[:p3]) # $ hasValueFlow=11 $ hasValueFlow=12 sink (kwargs[:p4]) end @@ -33,3 +33,9 @@ end kwargs(p1: taint(9), p2: taint(10), p3: taint(11), p4: "") args = { p3: taint(12), p4: "" } kwargs(p1: taint(13), **args) + +args = {:p1 => taint(14), :p2 => taint(15) } +kwargs(**args) + +args = {:p1 => taint(16) } +keyword(p2: taint(17), **args) diff --git a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected new file mode 100644 index 00000000000..a13e860bd04 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.expected @@ -0,0 +1,219 @@ +failures +edges +| pathname_flow.rb:4:10:4:33 | call to new : | pathname_flow.rb:5:10:5:11 | pn | +| pathname_flow.rb:4:23:4:32 | call to source : | pathname_flow.rb:4:10:4:33 | call to new : | +| pathname_flow.rb:9:7:9:30 | call to new : | pathname_flow.rb:11:8:11:12 | ... + ... | +| pathname_flow.rb:9:20:9:29 | call to source : | pathname_flow.rb:9:7:9:30 | call to new : | +| pathname_flow.rb:10:7:10:30 | call to new : | pathname_flow.rb:11:8:11:12 | ... + ... | +| pathname_flow.rb:10:20:10:29 | call to source : | pathname_flow.rb:10:7:10:30 | call to new : | +| pathname_flow.rb:15:8:15:31 | call to new : | pathname_flow.rb:16:8:16:9 | pn : | +| pathname_flow.rb:15:21:15:30 | call to source : | pathname_flow.rb:15:8:15:31 | call to new : | +| pathname_flow.rb:16:8:16:9 | pn : | pathname_flow.rb:16:8:16:17 | call to dirname | +| pathname_flow.rb:20:7:20:30 | call to new : | pathname_flow.rb:21:3:21:3 | a : | +| pathname_flow.rb:20:20:20:29 | call to source : | pathname_flow.rb:20:7:20:30 | call to new : | +| pathname_flow.rb:21:3:21:3 | a : | pathname_flow.rb:21:23:21:23 | x : | +| pathname_flow.rb:21:23:21:23 | x : | pathname_flow.rb:22:10:22:10 | x | +| pathname_flow.rb:27:7:27:30 | call to new : | pathname_flow.rb:28:8:28:8 | a : | +| pathname_flow.rb:27:20:27:29 | call to source : | pathname_flow.rb:27:7:27:30 | call to new : | +| pathname_flow.rb:28:8:28:8 | a : | pathname_flow.rb:28:8:28:22 | call to expand_path | +| pathname_flow.rb:32:7:32:30 | call to new : | pathname_flow.rb:35:8:35:8 | a : | +| pathname_flow.rb:32:20:32:29 | call to source : | pathname_flow.rb:32:7:32:30 | call to new : | +| pathname_flow.rb:34:7:34:30 | call to new : | pathname_flow.rb:35:18:35:18 | c : | +| pathname_flow.rb:34:20:34:29 | call to source : | pathname_flow.rb:34:7:34:30 | call to new : | +| pathname_flow.rb:35:8:35:8 | a : | pathname_flow.rb:35:8:35:19 | call to join | +| pathname_flow.rb:35:18:35:18 | c : | pathname_flow.rb:35:8:35:19 | call to join | +| pathname_flow.rb:39:7:39:30 | call to new : | pathname_flow.rb:40:8:40:8 | a : | +| pathname_flow.rb:39:20:39:29 | call to source : | pathname_flow.rb:39:7:39:30 | call to new : | +| pathname_flow.rb:40:8:40:8 | a : | pathname_flow.rb:40:8:40:17 | call to parent | +| pathname_flow.rb:44:7:44:30 | call to new : | pathname_flow.rb:45:8:45:8 | a : | +| pathname_flow.rb:44:20:44:29 | call to source : | pathname_flow.rb:44:7:44:30 | call to new : | +| pathname_flow.rb:45:8:45:8 | a : | pathname_flow.rb:45:8:45:19 | call to realpath | +| pathname_flow.rb:49:7:49:30 | call to new : | pathname_flow.rb:50:8:50:8 | a : | +| pathname_flow.rb:49:20:49:29 | call to source : | pathname_flow.rb:49:7:49:30 | call to new : | +| pathname_flow.rb:50:8:50:8 | a : | pathname_flow.rb:50:8:50:39 | call to relative_path_from | +| pathname_flow.rb:54:7:54:30 | call to new : | pathname_flow.rb:55:8:55:8 | a : | +| pathname_flow.rb:54:20:54:29 | call to source : | pathname_flow.rb:54:7:54:30 | call to new : | +| pathname_flow.rb:55:8:55:8 | a : | pathname_flow.rb:55:8:55:16 | call to to_path | +| pathname_flow.rb:59:7:59:30 | call to new : | pathname_flow.rb:60:8:60:8 | a : | +| pathname_flow.rb:59:20:59:29 | call to source : | pathname_flow.rb:59:7:59:30 | call to new : | +| pathname_flow.rb:60:8:60:8 | a : | pathname_flow.rb:60:8:60:13 | call to to_s | +| pathname_flow.rb:64:7:64:30 | call to new : | pathname_flow.rb:66:8:66:8 | b | +| pathname_flow.rb:64:20:64:29 | call to source : | pathname_flow.rb:64:7:64:30 | call to new : | +| pathname_flow.rb:70:7:70:30 | call to new : | pathname_flow.rb:72:8:72:8 | b | +| pathname_flow.rb:70:20:70:29 | call to source : | pathname_flow.rb:70:7:70:30 | call to new : | +| pathname_flow.rb:76:7:76:30 | call to new : | pathname_flow.rb:77:7:77:7 | a : | +| pathname_flow.rb:76:20:76:29 | call to source : | pathname_flow.rb:76:7:76:30 | call to new : | +| pathname_flow.rb:77:7:77:7 | a : | pathname_flow.rb:77:7:77:16 | call to basename : | +| pathname_flow.rb:77:7:77:16 | call to basename : | pathname_flow.rb:78:8:78:8 | b | +| pathname_flow.rb:82:7:82:30 | call to new : | pathname_flow.rb:83:7:83:7 | a : | +| pathname_flow.rb:82:20:82:29 | call to source : | pathname_flow.rb:82:7:82:30 | call to new : | +| pathname_flow.rb:83:7:83:7 | a : | pathname_flow.rb:83:7:83:17 | call to cleanpath : | +| pathname_flow.rb:83:7:83:17 | call to cleanpath : | pathname_flow.rb:84:8:84:8 | b | +| pathname_flow.rb:88:7:88:30 | call to new : | pathname_flow.rb:89:7:89:7 | a : | +| pathname_flow.rb:88:20:88:29 | call to source : | pathname_flow.rb:88:7:88:30 | call to new : | +| pathname_flow.rb:89:7:89:7 | a : | pathname_flow.rb:89:7:89:25 | call to sub : | +| pathname_flow.rb:89:7:89:25 | call to sub : | pathname_flow.rb:90:8:90:8 | b | +| pathname_flow.rb:94:7:94:30 | call to new : | pathname_flow.rb:95:7:95:7 | a : | +| pathname_flow.rb:94:20:94:29 | call to source : | pathname_flow.rb:94:7:94:30 | call to new : | +| pathname_flow.rb:95:7:95:7 | a : | pathname_flow.rb:95:7:95:23 | call to sub_ext : | +| pathname_flow.rb:95:7:95:23 | call to sub_ext : | pathname_flow.rb:96:8:96:8 | b | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:104:8:104:8 | b : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:107:8:107:8 | c : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:109:7:109:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:112:7:112:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:115:7:115:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:118:7:118:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:121:7:121:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:124:7:124:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:127:7:127:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:130:7:130:7 | a : | +| pathname_flow.rb:101:7:101:30 | call to new : | pathname_flow.rb:133:7:133:7 | a : | +| pathname_flow.rb:101:20:101:29 | call to source : | pathname_flow.rb:101:7:101:30 | call to new : | +| pathname_flow.rb:104:8:104:8 | b : | pathname_flow.rb:104:8:104:17 | call to realpath | +| pathname_flow.rb:107:8:107:8 | c : | pathname_flow.rb:107:8:107:17 | call to realpath | +| pathname_flow.rb:109:7:109:7 | a : | pathname_flow.rb:109:7:109:16 | call to basename : | +| pathname_flow.rb:109:7:109:16 | call to basename : | pathname_flow.rb:110:8:110:8 | d : | +| pathname_flow.rb:110:8:110:8 | d : | pathname_flow.rb:110:8:110:17 | call to realpath | +| pathname_flow.rb:112:7:112:7 | a : | pathname_flow.rb:112:7:112:17 | call to cleanpath : | +| pathname_flow.rb:112:7:112:17 | call to cleanpath : | pathname_flow.rb:113:8:113:8 | e : | +| pathname_flow.rb:113:8:113:8 | e : | pathname_flow.rb:113:8:113:17 | call to realpath | +| pathname_flow.rb:115:7:115:7 | a : | pathname_flow.rb:115:7:115:19 | call to expand_path : | +| pathname_flow.rb:115:7:115:19 | call to expand_path : | pathname_flow.rb:116:8:116:8 | f : | +| pathname_flow.rb:116:8:116:8 | f : | pathname_flow.rb:116:8:116:17 | call to realpath | +| pathname_flow.rb:118:7:118:7 | a : | pathname_flow.rb:118:7:118:19 | call to join : | +| pathname_flow.rb:118:7:118:19 | call to join : | pathname_flow.rb:119:8:119:8 | g : | +| pathname_flow.rb:119:8:119:8 | g : | pathname_flow.rb:119:8:119:17 | call to realpath | +| pathname_flow.rb:121:7:121:7 | a : | pathname_flow.rb:121:7:121:16 | call to realpath : | +| pathname_flow.rb:121:7:121:16 | call to realpath : | pathname_flow.rb:122:8:122:8 | h : | +| pathname_flow.rb:122:8:122:8 | h : | pathname_flow.rb:122:8:122:17 | call to realpath | +| pathname_flow.rb:124:7:124:7 | a : | pathname_flow.rb:124:7:124:38 | call to relative_path_from : | +| pathname_flow.rb:124:7:124:38 | call to relative_path_from : | pathname_flow.rb:125:8:125:8 | i : | +| pathname_flow.rb:125:8:125:8 | i : | pathname_flow.rb:125:8:125:17 | call to realpath | +| pathname_flow.rb:127:7:127:7 | a : | pathname_flow.rb:127:7:127:25 | call to sub : | +| pathname_flow.rb:127:7:127:25 | call to sub : | pathname_flow.rb:128:8:128:8 | j : | +| pathname_flow.rb:128:8:128:8 | j : | pathname_flow.rb:128:8:128:17 | call to realpath | +| pathname_flow.rb:130:7:130:7 | a : | pathname_flow.rb:130:7:130:23 | call to sub_ext : | +| pathname_flow.rb:130:7:130:23 | call to sub_ext : | pathname_flow.rb:131:8:131:8 | k : | +| pathname_flow.rb:131:8:131:8 | k : | pathname_flow.rb:131:8:131:17 | call to realpath | +| pathname_flow.rb:133:7:133:7 | a : | pathname_flow.rb:133:7:133:15 | call to to_path : | +| pathname_flow.rb:133:7:133:15 | call to to_path : | pathname_flow.rb:134:8:134:8 | l : | +| pathname_flow.rb:134:8:134:8 | l : | pathname_flow.rb:134:8:134:17 | call to realpath | +nodes +| pathname_flow.rb:4:10:4:33 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:4:23:4:32 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:5:10:5:11 | pn | semmle.label | pn | +| pathname_flow.rb:9:7:9:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:9:20:9:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:10:7:10:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:10:20:10:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:11:8:11:12 | ... + ... | semmle.label | ... + ... | +| pathname_flow.rb:15:8:15:31 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:15:21:15:30 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:16:8:16:9 | pn : | semmle.label | pn : | +| pathname_flow.rb:16:8:16:17 | call to dirname | semmle.label | call to dirname | +| pathname_flow.rb:20:7:20:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:20:20:20:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:21:3:21:3 | a : | semmle.label | a : | +| pathname_flow.rb:21:23:21:23 | x : | semmle.label | x : | +| pathname_flow.rb:22:10:22:10 | x | semmle.label | x | +| pathname_flow.rb:27:7:27:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:27:20:27:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:28:8:28:8 | a : | semmle.label | a : | +| pathname_flow.rb:28:8:28:22 | call to expand_path | semmle.label | call to expand_path | +| pathname_flow.rb:32:7:32:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:32:20:32:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:34:7:34:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:34:20:34:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:35:8:35:8 | a : | semmle.label | a : | +| pathname_flow.rb:35:8:35:19 | call to join | semmle.label | call to join | +| pathname_flow.rb:35:18:35:18 | c : | semmle.label | c : | +| pathname_flow.rb:39:7:39:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:39:20:39:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:40:8:40:8 | a : | semmle.label | a : | +| pathname_flow.rb:40:8:40:17 | call to parent | semmle.label | call to parent | +| pathname_flow.rb:44:7:44:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:44:20:44:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:45:8:45:8 | a : | semmle.label | a : | +| pathname_flow.rb:45:8:45:19 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:49:7:49:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:49:20:49:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:50:8:50:8 | a : | semmle.label | a : | +| pathname_flow.rb:50:8:50:39 | call to relative_path_from | semmle.label | call to relative_path_from | +| pathname_flow.rb:54:7:54:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:54:20:54:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:55:8:55:8 | a : | semmle.label | a : | +| pathname_flow.rb:55:8:55:16 | call to to_path | semmle.label | call to to_path | +| pathname_flow.rb:59:7:59:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:59:20:59:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:60:8:60:8 | a : | semmle.label | a : | +| pathname_flow.rb:60:8:60:13 | call to to_s | semmle.label | call to to_s | +| pathname_flow.rb:64:7:64:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:64:20:64:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:66:8:66:8 | b | semmle.label | b | +| pathname_flow.rb:70:7:70:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:70:20:70:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:72:8:72:8 | b | semmle.label | b | +| pathname_flow.rb:76:7:76:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:76:20:76:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:77:7:77:7 | a : | semmle.label | a : | +| pathname_flow.rb:77:7:77:16 | call to basename : | semmle.label | call to basename : | +| pathname_flow.rb:78:8:78:8 | b | semmle.label | b | +| pathname_flow.rb:82:7:82:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:82:20:82:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:83:7:83:7 | a : | semmle.label | a : | +| pathname_flow.rb:83:7:83:17 | call to cleanpath : | semmle.label | call to cleanpath : | +| pathname_flow.rb:84:8:84:8 | b | semmle.label | b | +| pathname_flow.rb:88:7:88:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:88:20:88:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:89:7:89:7 | a : | semmle.label | a : | +| pathname_flow.rb:89:7:89:25 | call to sub : | semmle.label | call to sub : | +| pathname_flow.rb:90:8:90:8 | b | semmle.label | b | +| pathname_flow.rb:94:7:94:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:94:20:94:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:95:7:95:7 | a : | semmle.label | a : | +| pathname_flow.rb:95:7:95:23 | call to sub_ext : | semmle.label | call to sub_ext : | +| pathname_flow.rb:96:8:96:8 | b | semmle.label | b | +| pathname_flow.rb:101:7:101:30 | call to new : | semmle.label | call to new : | +| pathname_flow.rb:101:20:101:29 | call to source : | semmle.label | call to source : | +| pathname_flow.rb:104:8:104:8 | b : | semmle.label | b : | +| pathname_flow.rb:104:8:104:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:107:8:107:8 | c : | semmle.label | c : | +| pathname_flow.rb:107:8:107:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:109:7:109:7 | a : | semmle.label | a : | +| pathname_flow.rb:109:7:109:16 | call to basename : | semmle.label | call to basename : | +| pathname_flow.rb:110:8:110:8 | d : | semmle.label | d : | +| pathname_flow.rb:110:8:110:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:112:7:112:7 | a : | semmle.label | a : | +| pathname_flow.rb:112:7:112:17 | call to cleanpath : | semmle.label | call to cleanpath : | +| pathname_flow.rb:113:8:113:8 | e : | semmle.label | e : | +| pathname_flow.rb:113:8:113:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:115:7:115:7 | a : | semmle.label | a : | +| pathname_flow.rb:115:7:115:19 | call to expand_path : | semmle.label | call to expand_path : | +| pathname_flow.rb:116:8:116:8 | f : | semmle.label | f : | +| pathname_flow.rb:116:8:116:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:118:7:118:7 | a : | semmle.label | a : | +| pathname_flow.rb:118:7:118:19 | call to join : | semmle.label | call to join : | +| pathname_flow.rb:119:8:119:8 | g : | semmle.label | g : | +| pathname_flow.rb:119:8:119:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:121:7:121:7 | a : | semmle.label | a : | +| pathname_flow.rb:121:7:121:16 | call to realpath : | semmle.label | call to realpath : | +| pathname_flow.rb:122:8:122:8 | h : | semmle.label | h : | +| pathname_flow.rb:122:8:122:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:124:7:124:7 | a : | semmle.label | a : | +| pathname_flow.rb:124:7:124:38 | call to relative_path_from : | semmle.label | call to relative_path_from : | +| pathname_flow.rb:125:8:125:8 | i : | semmle.label | i : | +| pathname_flow.rb:125:8:125:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:127:7:127:7 | a : | semmle.label | a : | +| pathname_flow.rb:127:7:127:25 | call to sub : | semmle.label | call to sub : | +| pathname_flow.rb:128:8:128:8 | j : | semmle.label | j : | +| pathname_flow.rb:128:8:128:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:130:7:130:7 | a : | semmle.label | a : | +| pathname_flow.rb:130:7:130:23 | call to sub_ext : | semmle.label | call to sub_ext : | +| pathname_flow.rb:131:8:131:8 | k : | semmle.label | k : | +| pathname_flow.rb:131:8:131:17 | call to realpath | semmle.label | call to realpath | +| pathname_flow.rb:133:7:133:7 | a : | semmle.label | a : | +| pathname_flow.rb:133:7:133:15 | call to to_path : | semmle.label | call to to_path : | +| pathname_flow.rb:134:8:134:8 | l : | semmle.label | l : | +| pathname_flow.rb:134:8:134:17 | call to realpath | semmle.label | call to realpath | +subpaths +#select diff --git a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql new file mode 100644 index 00000000000..4e812d32daa --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathame-flow.ql @@ -0,0 +1,11 @@ +/** + * @kind path-problem + */ + +import ruby +import TestUtilities.InlineFlowTest +import PathGraph + +from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultValueFlowConf conf +where conf.hasFlowPath(source, sink) +select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb new file mode 100644 index 00000000000..dab4adec6c6 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/pathname-flow/pathname_flow.rb @@ -0,0 +1,135 @@ +require 'pathname' + +def m_new + pn = Pathname.new(source 'a') + sink pn # $ hasTaintFlow=a +end + +def m_plus + a = Pathname.new(source 'a') + b = Pathname.new(source 'b') + sink(a + b) # $ hasTaintFlow=a $ hasTaintFlow=b +end + +def m_dirname + pn = Pathname.new(source 'a') + sink pn.dirname # $ hasTaintFlow=a +end + +def m_each_filename + a = Pathname.new(source 'a') + a.each_filename do |x| + sink x # $ hasTaintFlow=a + end +end + +def m_expand_path + a = Pathname.new(source 'a') + sink a.expand_path() # $ hasTaintFlow=a +end + +def m_join + a = Pathname.new(source 'a') + b = Pathname.new('foo') + c = Pathname.new(source 'c') + sink a.join(b, c) # $ hasTaintFlow=a $ hasTaintFlow=c +end + +def m_parent + a = Pathname.new(source 'a') + sink a.parent() # $ hasTaintFlow=a +end + +def m_realpath + a = Pathname.new(source 'a') + sink a.realpath() # $ hasTaintFlow=a +end + +def m_relative_path_from + a = Pathname.new(source 'a') + sink a.relative_path_from('/foo/bar') # $ hasTaintFlow=a +end + +def m_to_path + a = Pathname.new(source 'a') + sink a.to_path # $ hasTaintFlow=a +end + +def m_to_s + a = Pathname.new(source 'a') + sink a.to_s # $ hasTaintFlow=a +end + +def m_plus + a = Pathname.new(source 'a') + b = a + 'foo' + sink b # $ hasTaintFlow=a +end + +def m_slash + a = Pathname.new(source 'a') + b = a / 'foo' + sink b # $ hasTaintFlow=a +end + +def m_basename + a = Pathname.new(source 'a') + b = a.basename + sink b # $ hasTaintFlow=a +end + +def m_cleanpath + a = Pathname.new(source 'a') + b = a.cleanpath + sink b # $ hasTaintFlow=a +end + +def m_sub + a = Pathname.new(source 'a') + b = a.sub('foo', 'bar') + sink b # $ hasTaintFlow=a +end + +def m_sub_ext + a = Pathname.new(source 'a') + b = a.sub_ext('.txt') + sink b # $ hasTaintFlow=a +end + +# Test flow through intermediate pathnames +def intermediate_pathnames + a = Pathname.new(source 'a') + + b = a + 'foo' + sink b.realpath # $ hasTaintFlow=a + + c = a / 'foo' + sink c.realpath # $ hasTaintFlow=a + + d = a.basename + sink d.realpath # $ hasTaintFlow=a + + e = a.cleanpath + sink e.realpath # $ hasTaintFlow=a + + f = a.expand_path + sink f.realpath # $ hasTaintFlow=a + + g = a.join('foo') + sink g.realpath # $ hasTaintFlow=a + + h = a.realpath + sink h.realpath # $ hasTaintFlow=a + + i = a.relative_path_from('/foo/bar') + sink i.realpath # $ hasTaintFlow=a + + j = a.sub('foo', 'bar') + sink j.realpath # $ hasTaintFlow=a + + k = a.sub_ext('.txt') + sink k.realpath # $ hasTaintFlow=a + + l = a.to_path + sink l.realpath # $ hasTaintFlow=a +end \ No newline at end of file diff --git a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected index 595ec706db6..1669a6b07fa 100644 --- a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected +++ b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected @@ -19,19 +19,19 @@ edges | summaries.rb:1:11:1:36 | call to identity : | summaries.rb:37:36:37:42 | tainted | | summaries.rb:1:11:1:36 | call to identity : | summaries.rb:37:36:37:42 | tainted | | summaries.rb:1:11:1:36 | call to identity : | summaries.rb:51:24:51:30 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:54:22:54:28 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:55:17:55:23 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:57:27:57:33 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:61:32:61:38 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:63:23:63:29 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:102:16:102:22 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:108:14:108:20 | tainted : | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:111:16:111:22 | tainted | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:111:16:111:22 | tainted | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:112:21:112:27 | tainted | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:112:21:112:27 | tainted | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:115:26:115:32 | tainted | -| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:115:26:115:32 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:56:22:56:28 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:57:17:57:23 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:59:27:59:33 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:63:32:63:38 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:65:23:65:29 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:104:16:104:22 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:110:14:110:20 | tainted : | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:113:16:113:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:113:16:113:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:114:21:114:27 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:114:21:114:27 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:117:26:117:32 | tainted | +| summaries.rb:1:11:1:36 | call to identity : | summaries.rb:117:26:117:32 | tainted | | summaries.rb:1:20:1:36 | call to source : | summaries.rb:1:11:1:36 | call to identity : | | summaries.rb:1:20:1:36 | call to source : | summaries.rb:1:11:1:36 | call to identity : | | summaries.rb:4:12:7:3 | call to apply_block : | summaries.rb:9:6:9:13 | tainted2 | @@ -64,55 +64,58 @@ edges | summaries.rb:44:8:44:8 | t : | summaries.rb:44:8:44:27 | call to matchedByNameRcv | | summaries.rb:48:24:48:41 | call to source : | summaries.rb:48:8:48:42 | call to preserveTaint | | summaries.rb:51:24:51:30 | tainted : | summaries.rb:51:6:51:31 | call to namedArg | -| summaries.rb:54:22:54:28 | tainted : | summaries.rb:54:6:54:29 | call to anyArg | -| summaries.rb:55:17:55:23 | tainted : | summaries.rb:55:6:55:24 | call to anyArg | -| summaries.rb:57:27:57:33 | tainted : | summaries.rb:57:6:57:34 | call to anyNamedArg | -| summaries.rb:61:32:61:38 | tainted : | summaries.rb:61:6:61:39 | call to anyPositionFromOne | -| summaries.rb:63:23:63:29 | tainted : | summaries.rb:63:40:63:40 | x : | -| summaries.rb:63:40:63:40 | x : | summaries.rb:64:8:64:8 | x | -| summaries.rb:71:24:71:53 | call to source : | summaries.rb:71:8:71:54 | call to preserveTaint | -| summaries.rb:74:26:74:56 | call to source : | summaries.rb:74:8:74:57 | call to preserveTaint | -| summaries.rb:77:15:77:29 | call to source : | summaries.rb:79:6:79:6 | a [element 1] : | -| summaries.rb:77:15:77:29 | call to source : | summaries.rb:79:6:79:6 | a [element 1] : | -| summaries.rb:77:15:77:29 | call to source : | summaries.rb:81:5:81:5 | a [element 1] : | -| summaries.rb:77:15:77:29 | call to source : | summaries.rb:81:5:81:5 | a [element 1] : | -| summaries.rb:77:32:77:46 | call to source : | summaries.rb:80:6:80:6 | a [element 2] : | -| summaries.rb:77:32:77:46 | call to source : | summaries.rb:80:6:80:6 | a [element 2] : | -| summaries.rb:77:32:77:46 | call to source : | summaries.rb:85:1:85:1 | a [element 2] : | -| summaries.rb:77:32:77:46 | call to source : | summaries.rb:85:1:85:1 | a [element 2] : | -| summaries.rb:79:6:79:6 | a [element 1] : | summaries.rb:79:6:79:9 | ...[...] | -| summaries.rb:79:6:79:6 | a [element 1] : | summaries.rb:79:6:79:9 | ...[...] | -| summaries.rb:80:6:80:6 | a [element 2] : | summaries.rb:80:6:80:9 | ...[...] | -| summaries.rb:80:6:80:6 | a [element 2] : | summaries.rb:80:6:80:9 | ...[...] | -| summaries.rb:81:5:81:5 | a [element 1] : | summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | -| summaries.rb:81:5:81:5 | a [element 1] : | summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | -| summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | summaries.rb:83:6:83:6 | b [element 1] : | -| summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | summaries.rb:83:6:83:6 | b [element 1] : | -| summaries.rb:83:6:83:6 | b [element 1] : | summaries.rb:83:6:83:9 | ...[...] | -| summaries.rb:83:6:83:6 | b [element 1] : | summaries.rb:83:6:83:9 | ...[...] | -| summaries.rb:85:1:85:1 | [post] a [element 2] : | summaries.rb:88:6:88:6 | a [element 2] : | -| summaries.rb:85:1:85:1 | [post] a [element 2] : | summaries.rb:88:6:88:6 | a [element 2] : | -| summaries.rb:85:1:85:1 | a [element 2] : | summaries.rb:85:1:85:1 | [post] a [element 2] : | -| summaries.rb:85:1:85:1 | a [element 2] : | summaries.rb:85:1:85:1 | [post] a [element 2] : | -| summaries.rb:88:6:88:6 | a [element 2] : | summaries.rb:88:6:88:9 | ...[...] | -| summaries.rb:88:6:88:6 | a [element 2] : | summaries.rb:88:6:88:9 | ...[...] | -| summaries.rb:91:1:91:1 | [post] x [@value] : | summaries.rb:92:6:92:6 | x [@value] : | -| summaries.rb:91:1:91:1 | [post] x [@value] : | summaries.rb:92:6:92:6 | x [@value] : | -| summaries.rb:91:13:91:26 | call to source : | summaries.rb:91:1:91:1 | [post] x [@value] : | -| summaries.rb:91:13:91:26 | call to source : | summaries.rb:91:1:91:1 | [post] x [@value] : | -| summaries.rb:92:6:92:6 | x [@value] : | summaries.rb:92:6:92:16 | call to get_value | -| summaries.rb:92:6:92:6 | x [@value] : | summaries.rb:92:6:92:16 | call to get_value | -| summaries.rb:102:16:102:22 | [post] tainted : | summaries.rb:108:14:108:20 | tainted : | -| summaries.rb:102:16:102:22 | [post] tainted : | summaries.rb:111:16:111:22 | tainted | -| summaries.rb:102:16:102:22 | [post] tainted : | summaries.rb:112:21:112:27 | tainted | -| summaries.rb:102:16:102:22 | [post] tainted : | summaries.rb:115:26:115:32 | tainted | -| summaries.rb:102:16:102:22 | tainted : | summaries.rb:102:16:102:22 | [post] tainted : | -| summaries.rb:102:16:102:22 | tainted : | summaries.rb:102:25:102:25 | [post] y : | -| summaries.rb:102:16:102:22 | tainted : | summaries.rb:102:33:102:33 | [post] z : | -| summaries.rb:102:25:102:25 | [post] y : | summaries.rb:104:6:104:6 | y | -| summaries.rb:102:33:102:33 | [post] z : | summaries.rb:105:6:105:6 | z | -| summaries.rb:108:1:108:1 | [post] x : | summaries.rb:109:6:109:6 | x | -| summaries.rb:108:14:108:20 | tainted : | summaries.rb:108:1:108:1 | [post] x : | +| summaries.rb:53:15:53:31 | call to source : | summaries.rb:54:21:54:24 | args [element :foo] : | +| summaries.rb:54:19:54:24 | ** ... [element :foo] : | summaries.rb:54:6:54:25 | call to namedArg | +| summaries.rb:54:21:54:24 | args [element :foo] : | summaries.rb:54:19:54:24 | ** ... [element :foo] : | +| summaries.rb:56:22:56:28 | tainted : | summaries.rb:56:6:56:29 | call to anyArg | +| summaries.rb:57:17:57:23 | tainted : | summaries.rb:57:6:57:24 | call to anyArg | +| summaries.rb:59:27:59:33 | tainted : | summaries.rb:59:6:59:34 | call to anyNamedArg | +| summaries.rb:63:32:63:38 | tainted : | summaries.rb:63:6:63:39 | call to anyPositionFromOne | +| summaries.rb:65:23:65:29 | tainted : | summaries.rb:65:40:65:40 | x : | +| summaries.rb:65:40:65:40 | x : | summaries.rb:66:8:66:8 | x | +| summaries.rb:73:24:73:53 | call to source : | summaries.rb:73:8:73:54 | call to preserveTaint | +| summaries.rb:76:26:76:56 | call to source : | summaries.rb:76:8:76:57 | call to preserveTaint | +| summaries.rb:79:15:79:29 | call to source : | summaries.rb:81:6:81:6 | a [element 1] : | +| summaries.rb:79:15:79:29 | call to source : | summaries.rb:81:6:81:6 | a [element 1] : | +| summaries.rb:79:15:79:29 | call to source : | summaries.rb:83:5:83:5 | a [element 1] : | +| summaries.rb:79:15:79:29 | call to source : | summaries.rb:83:5:83:5 | a [element 1] : | +| summaries.rb:79:32:79:46 | call to source : | summaries.rb:82:6:82:6 | a [element 2] : | +| summaries.rb:79:32:79:46 | call to source : | summaries.rb:82:6:82:6 | a [element 2] : | +| summaries.rb:79:32:79:46 | call to source : | summaries.rb:87:1:87:1 | a [element 2] : | +| summaries.rb:79:32:79:46 | call to source : | summaries.rb:87:1:87:1 | a [element 2] : | +| summaries.rb:81:6:81:6 | a [element 1] : | summaries.rb:81:6:81:9 | ...[...] | +| summaries.rb:81:6:81:6 | a [element 1] : | summaries.rb:81:6:81:9 | ...[...] | +| summaries.rb:82:6:82:6 | a [element 2] : | summaries.rb:82:6:82:9 | ...[...] | +| summaries.rb:82:6:82:6 | a [element 2] : | summaries.rb:82:6:82:9 | ...[...] | +| summaries.rb:83:5:83:5 | a [element 1] : | summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | +| summaries.rb:83:5:83:5 | a [element 1] : | summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | +| summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | summaries.rb:85:6:85:6 | b [element 1] : | +| summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | summaries.rb:85:6:85:6 | b [element 1] : | +| summaries.rb:85:6:85:6 | b [element 1] : | summaries.rb:85:6:85:9 | ...[...] | +| summaries.rb:85:6:85:6 | b [element 1] : | summaries.rb:85:6:85:9 | ...[...] | +| summaries.rb:87:1:87:1 | [post] a [element 2] : | summaries.rb:90:6:90:6 | a [element 2] : | +| summaries.rb:87:1:87:1 | [post] a [element 2] : | summaries.rb:90:6:90:6 | a [element 2] : | +| summaries.rb:87:1:87:1 | a [element 2] : | summaries.rb:87:1:87:1 | [post] a [element 2] : | +| summaries.rb:87:1:87:1 | a [element 2] : | summaries.rb:87:1:87:1 | [post] a [element 2] : | +| summaries.rb:90:6:90:6 | a [element 2] : | summaries.rb:90:6:90:9 | ...[...] | +| summaries.rb:90:6:90:6 | a [element 2] : | summaries.rb:90:6:90:9 | ...[...] | +| summaries.rb:93:1:93:1 | [post] x [@value] : | summaries.rb:94:6:94:6 | x [@value] : | +| summaries.rb:93:1:93:1 | [post] x [@value] : | summaries.rb:94:6:94:6 | x [@value] : | +| summaries.rb:93:13:93:26 | call to source : | summaries.rb:93:1:93:1 | [post] x [@value] : | +| summaries.rb:93:13:93:26 | call to source : | summaries.rb:93:1:93:1 | [post] x [@value] : | +| summaries.rb:94:6:94:6 | x [@value] : | summaries.rb:94:6:94:16 | call to get_value | +| summaries.rb:94:6:94:6 | x [@value] : | summaries.rb:94:6:94:16 | call to get_value | +| summaries.rb:104:16:104:22 | [post] tainted : | summaries.rb:110:14:110:20 | tainted : | +| summaries.rb:104:16:104:22 | [post] tainted : | summaries.rb:113:16:113:22 | tainted | +| summaries.rb:104:16:104:22 | [post] tainted : | summaries.rb:114:21:114:27 | tainted | +| summaries.rb:104:16:104:22 | [post] tainted : | summaries.rb:117:26:117:32 | tainted | +| summaries.rb:104:16:104:22 | tainted : | summaries.rb:104:16:104:22 | [post] tainted : | +| summaries.rb:104:16:104:22 | tainted : | summaries.rb:104:25:104:25 | [post] y : | +| summaries.rb:104:16:104:22 | tainted : | summaries.rb:104:33:104:33 | [post] z : | +| summaries.rb:104:25:104:25 | [post] y : | summaries.rb:106:6:106:6 | y | +| summaries.rb:104:33:104:33 | [post] z : | summaries.rb:107:6:107:6 | z | +| summaries.rb:110:1:110:1 | [post] x : | summaries.rb:111:6:111:6 | x | +| summaries.rb:110:14:110:20 | tainted : | summaries.rb:110:1:110:1 | [post] x : | nodes | summaries.rb:1:11:1:36 | call to identity : | semmle.label | call to identity : | | summaries.rb:1:11:1:36 | call to identity : | semmle.label | call to identity : | @@ -169,72 +172,76 @@ nodes | summaries.rb:48:24:48:41 | call to source : | semmle.label | call to source : | | summaries.rb:51:6:51:31 | call to namedArg | semmle.label | call to namedArg | | summaries.rb:51:24:51:30 | tainted : | semmle.label | tainted : | -| summaries.rb:54:6:54:29 | call to anyArg | semmle.label | call to anyArg | -| summaries.rb:54:22:54:28 | tainted : | semmle.label | tainted : | -| summaries.rb:55:6:55:24 | call to anyArg | semmle.label | call to anyArg | -| summaries.rb:55:17:55:23 | tainted : | semmle.label | tainted : | -| summaries.rb:57:6:57:34 | call to anyNamedArg | semmle.label | call to anyNamedArg | -| summaries.rb:57:27:57:33 | tainted : | semmle.label | tainted : | -| summaries.rb:61:6:61:39 | call to anyPositionFromOne | semmle.label | call to anyPositionFromOne | -| summaries.rb:61:32:61:38 | tainted : | semmle.label | tainted : | -| summaries.rb:63:23:63:29 | tainted : | semmle.label | tainted : | -| summaries.rb:63:40:63:40 | x : | semmle.label | x : | -| summaries.rb:64:8:64:8 | x | semmle.label | x | -| summaries.rb:71:8:71:54 | call to preserveTaint | semmle.label | call to preserveTaint | -| summaries.rb:71:24:71:53 | call to source : | semmle.label | call to source : | -| summaries.rb:74:8:74:57 | call to preserveTaint | semmle.label | call to preserveTaint | -| summaries.rb:74:26:74:56 | call to source : | semmle.label | call to source : | -| summaries.rb:77:15:77:29 | call to source : | semmle.label | call to source : | -| summaries.rb:77:15:77:29 | call to source : | semmle.label | call to source : | -| summaries.rb:77:32:77:46 | call to source : | semmle.label | call to source : | -| summaries.rb:77:32:77:46 | call to source : | semmle.label | call to source : | -| summaries.rb:79:6:79:6 | a [element 1] : | semmle.label | a [element 1] : | -| summaries.rb:79:6:79:6 | a [element 1] : | semmle.label | a [element 1] : | -| summaries.rb:79:6:79:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:79:6:79:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:80:6:80:6 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:80:6:80:6 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:80:6:80:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:80:6:80:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:81:5:81:5 | a [element 1] : | semmle.label | a [element 1] : | -| summaries.rb:81:5:81:5 | a [element 1] : | semmle.label | a [element 1] : | -| summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | semmle.label | call to withElementOne [element 1] : | -| summaries.rb:81:5:81:22 | call to withElementOne [element 1] : | semmle.label | call to withElementOne [element 1] : | -| summaries.rb:83:6:83:6 | b [element 1] : | semmle.label | b [element 1] : | -| summaries.rb:83:6:83:6 | b [element 1] : | semmle.label | b [element 1] : | -| summaries.rb:83:6:83:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:83:6:83:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:85:1:85:1 | [post] a [element 2] : | semmle.label | [post] a [element 2] : | -| summaries.rb:85:1:85:1 | [post] a [element 2] : | semmle.label | [post] a [element 2] : | -| summaries.rb:85:1:85:1 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:85:1:85:1 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:88:6:88:6 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:88:6:88:6 | a [element 2] : | semmle.label | a [element 2] : | -| summaries.rb:88:6:88:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:88:6:88:9 | ...[...] | semmle.label | ...[...] | -| summaries.rb:91:1:91:1 | [post] x [@value] : | semmle.label | [post] x [@value] : | -| summaries.rb:91:1:91:1 | [post] x [@value] : | semmle.label | [post] x [@value] : | -| summaries.rb:91:13:91:26 | call to source : | semmle.label | call to source : | -| summaries.rb:91:13:91:26 | call to source : | semmle.label | call to source : | -| summaries.rb:92:6:92:6 | x [@value] : | semmle.label | x [@value] : | -| summaries.rb:92:6:92:6 | x [@value] : | semmle.label | x [@value] : | -| summaries.rb:92:6:92:16 | call to get_value | semmle.label | call to get_value | -| summaries.rb:92:6:92:16 | call to get_value | semmle.label | call to get_value | -| summaries.rb:102:16:102:22 | [post] tainted : | semmle.label | [post] tainted : | -| summaries.rb:102:16:102:22 | tainted : | semmle.label | tainted : | -| summaries.rb:102:25:102:25 | [post] y : | semmle.label | [post] y : | -| summaries.rb:102:33:102:33 | [post] z : | semmle.label | [post] z : | -| summaries.rb:104:6:104:6 | y | semmle.label | y | -| summaries.rb:105:6:105:6 | z | semmle.label | z | -| summaries.rb:108:1:108:1 | [post] x : | semmle.label | [post] x : | -| summaries.rb:108:14:108:20 | tainted : | semmle.label | tainted : | -| summaries.rb:109:6:109:6 | x | semmle.label | x | -| summaries.rb:111:16:111:22 | tainted | semmle.label | tainted | -| summaries.rb:111:16:111:22 | tainted | semmle.label | tainted | -| summaries.rb:112:21:112:27 | tainted | semmle.label | tainted | -| summaries.rb:112:21:112:27 | tainted | semmle.label | tainted | -| summaries.rb:115:26:115:32 | tainted | semmle.label | tainted | -| summaries.rb:115:26:115:32 | tainted | semmle.label | tainted | +| summaries.rb:53:15:53:31 | call to source : | semmle.label | call to source : | +| summaries.rb:54:6:54:25 | call to namedArg | semmle.label | call to namedArg | +| summaries.rb:54:19:54:24 | ** ... [element :foo] : | semmle.label | ** ... [element :foo] : | +| summaries.rb:54:21:54:24 | args [element :foo] : | semmle.label | args [element :foo] : | +| summaries.rb:56:6:56:29 | call to anyArg | semmle.label | call to anyArg | +| summaries.rb:56:22:56:28 | tainted : | semmle.label | tainted : | +| summaries.rb:57:6:57:24 | call to anyArg | semmle.label | call to anyArg | +| summaries.rb:57:17:57:23 | tainted : | semmle.label | tainted : | +| summaries.rb:59:6:59:34 | call to anyNamedArg | semmle.label | call to anyNamedArg | +| summaries.rb:59:27:59:33 | tainted : | semmle.label | tainted : | +| summaries.rb:63:6:63:39 | call to anyPositionFromOne | semmle.label | call to anyPositionFromOne | +| summaries.rb:63:32:63:38 | tainted : | semmle.label | tainted : | +| summaries.rb:65:23:65:29 | tainted : | semmle.label | tainted : | +| summaries.rb:65:40:65:40 | x : | semmle.label | x : | +| summaries.rb:66:8:66:8 | x | semmle.label | x | +| summaries.rb:73:8:73:54 | call to preserveTaint | semmle.label | call to preserveTaint | +| summaries.rb:73:24:73:53 | call to source : | semmle.label | call to source : | +| summaries.rb:76:8:76:57 | call to preserveTaint | semmle.label | call to preserveTaint | +| summaries.rb:76:26:76:56 | call to source : | semmle.label | call to source : | +| summaries.rb:79:15:79:29 | call to source : | semmle.label | call to source : | +| summaries.rb:79:15:79:29 | call to source : | semmle.label | call to source : | +| summaries.rb:79:32:79:46 | call to source : | semmle.label | call to source : | +| summaries.rb:79:32:79:46 | call to source : | semmle.label | call to source : | +| summaries.rb:81:6:81:6 | a [element 1] : | semmle.label | a [element 1] : | +| summaries.rb:81:6:81:6 | a [element 1] : | semmle.label | a [element 1] : | +| summaries.rb:81:6:81:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:81:6:81:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:82:6:82:6 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:82:6:82:6 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:82:6:82:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:82:6:82:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:83:5:83:5 | a [element 1] : | semmle.label | a [element 1] : | +| summaries.rb:83:5:83:5 | a [element 1] : | semmle.label | a [element 1] : | +| summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | semmle.label | call to withElementOne [element 1] : | +| summaries.rb:83:5:83:22 | call to withElementOne [element 1] : | semmle.label | call to withElementOne [element 1] : | +| summaries.rb:85:6:85:6 | b [element 1] : | semmle.label | b [element 1] : | +| summaries.rb:85:6:85:6 | b [element 1] : | semmle.label | b [element 1] : | +| summaries.rb:85:6:85:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:85:6:85:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:87:1:87:1 | [post] a [element 2] : | semmle.label | [post] a [element 2] : | +| summaries.rb:87:1:87:1 | [post] a [element 2] : | semmle.label | [post] a [element 2] : | +| summaries.rb:87:1:87:1 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:87:1:87:1 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:90:6:90:6 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:90:6:90:6 | a [element 2] : | semmle.label | a [element 2] : | +| summaries.rb:90:6:90:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:90:6:90:9 | ...[...] | semmle.label | ...[...] | +| summaries.rb:93:1:93:1 | [post] x [@value] : | semmle.label | [post] x [@value] : | +| summaries.rb:93:1:93:1 | [post] x [@value] : | semmle.label | [post] x [@value] : | +| summaries.rb:93:13:93:26 | call to source : | semmle.label | call to source : | +| summaries.rb:93:13:93:26 | call to source : | semmle.label | call to source : | +| summaries.rb:94:6:94:6 | x [@value] : | semmle.label | x [@value] : | +| summaries.rb:94:6:94:6 | x [@value] : | semmle.label | x [@value] : | +| summaries.rb:94:6:94:16 | call to get_value | semmle.label | call to get_value | +| summaries.rb:94:6:94:16 | call to get_value | semmle.label | call to get_value | +| summaries.rb:104:16:104:22 | [post] tainted : | semmle.label | [post] tainted : | +| summaries.rb:104:16:104:22 | tainted : | semmle.label | tainted : | +| summaries.rb:104:25:104:25 | [post] y : | semmle.label | [post] y : | +| summaries.rb:104:33:104:33 | [post] z : | semmle.label | [post] z : | +| summaries.rb:106:6:106:6 | y | semmle.label | y | +| summaries.rb:107:6:107:6 | z | semmle.label | z | +| summaries.rb:110:1:110:1 | [post] x : | semmle.label | [post] x : | +| summaries.rb:110:14:110:20 | tainted : | semmle.label | tainted : | +| summaries.rb:111:6:111:6 | x | semmle.label | x | +| summaries.rb:113:16:113:22 | tainted | semmle.label | tainted | +| summaries.rb:113:16:113:22 | tainted | semmle.label | tainted | +| summaries.rb:114:21:114:27 | tainted | semmle.label | tainted | +| summaries.rb:114:21:114:27 | tainted | semmle.label | tainted | +| summaries.rb:117:26:117:32 | tainted | semmle.label | tainted | +| summaries.rb:117:26:117:32 | tainted | semmle.label | tainted | subpaths invalidSpecComponent #select @@ -265,32 +272,33 @@ invalidSpecComponent | summaries.rb:44:8:44:27 | call to matchedByNameRcv | summaries.rb:40:7:40:17 | call to source : | summaries.rb:44:8:44:27 | call to matchedByNameRcv | $@ | summaries.rb:40:7:40:17 | call to source : | call to source : | | summaries.rb:48:8:48:42 | call to preserveTaint | summaries.rb:48:24:48:41 | call to source : | summaries.rb:48:8:48:42 | call to preserveTaint | $@ | summaries.rb:48:24:48:41 | call to source : | call to source : | | summaries.rb:51:6:51:31 | call to namedArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:51:6:51:31 | call to namedArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:54:6:54:29 | call to anyArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:54:6:54:29 | call to anyArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:55:6:55:24 | call to anyArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:55:6:55:24 | call to anyArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:57:6:57:34 | call to anyNamedArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:57:6:57:34 | call to anyNamedArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:61:6:61:39 | call to anyPositionFromOne | summaries.rb:1:20:1:36 | call to source : | summaries.rb:61:6:61:39 | call to anyPositionFromOne | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:64:8:64:8 | x | summaries.rb:1:20:1:36 | call to source : | summaries.rb:64:8:64:8 | x | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:71:8:71:54 | call to preserveTaint | summaries.rb:71:24:71:53 | call to source : | summaries.rb:71:8:71:54 | call to preserveTaint | $@ | summaries.rb:71:24:71:53 | call to source : | call to source : | -| summaries.rb:74:8:74:57 | call to preserveTaint | summaries.rb:74:26:74:56 | call to source : | summaries.rb:74:8:74:57 | call to preserveTaint | $@ | summaries.rb:74:26:74:56 | call to source : | call to source : | -| summaries.rb:79:6:79:9 | ...[...] | summaries.rb:77:15:77:29 | call to source : | summaries.rb:79:6:79:9 | ...[...] | $@ | summaries.rb:77:15:77:29 | call to source : | call to source : | -| summaries.rb:79:6:79:9 | ...[...] | summaries.rb:77:15:77:29 | call to source : | summaries.rb:79:6:79:9 | ...[...] | $@ | summaries.rb:77:15:77:29 | call to source : | call to source : | -| summaries.rb:80:6:80:9 | ...[...] | summaries.rb:77:32:77:46 | call to source : | summaries.rb:80:6:80:9 | ...[...] | $@ | summaries.rb:77:32:77:46 | call to source : | call to source : | -| summaries.rb:80:6:80:9 | ...[...] | summaries.rb:77:32:77:46 | call to source : | summaries.rb:80:6:80:9 | ...[...] | $@ | summaries.rb:77:32:77:46 | call to source : | call to source : | -| summaries.rb:83:6:83:9 | ...[...] | summaries.rb:77:15:77:29 | call to source : | summaries.rb:83:6:83:9 | ...[...] | $@ | summaries.rb:77:15:77:29 | call to source : | call to source : | -| summaries.rb:83:6:83:9 | ...[...] | summaries.rb:77:15:77:29 | call to source : | summaries.rb:83:6:83:9 | ...[...] | $@ | summaries.rb:77:15:77:29 | call to source : | call to source : | -| summaries.rb:88:6:88:9 | ...[...] | summaries.rb:77:32:77:46 | call to source : | summaries.rb:88:6:88:9 | ...[...] | $@ | summaries.rb:77:32:77:46 | call to source : | call to source : | -| summaries.rb:88:6:88:9 | ...[...] | summaries.rb:77:32:77:46 | call to source : | summaries.rb:88:6:88:9 | ...[...] | $@ | summaries.rb:77:32:77:46 | call to source : | call to source : | -| summaries.rb:92:6:92:16 | call to get_value | summaries.rb:91:13:91:26 | call to source : | summaries.rb:92:6:92:16 | call to get_value | $@ | summaries.rb:91:13:91:26 | call to source : | call to source : | -| summaries.rb:92:6:92:16 | call to get_value | summaries.rb:91:13:91:26 | call to source : | summaries.rb:92:6:92:16 | call to get_value | $@ | summaries.rb:91:13:91:26 | call to source : | call to source : | -| summaries.rb:104:6:104:6 | y | summaries.rb:1:20:1:36 | call to source : | summaries.rb:104:6:104:6 | y | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:105:6:105:6 | z | summaries.rb:1:20:1:36 | call to source : | summaries.rb:105:6:105:6 | z | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:109:6:109:6 | x | summaries.rb:1:20:1:36 | call to source : | summaries.rb:109:6:109:6 | x | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:111:16:111:22 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:111:16:111:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:111:16:111:22 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:111:16:111:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:112:21:112:27 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:112:21:112:27 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:112:21:112:27 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:112:21:112:27 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:115:26:115:32 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:115:26:115:32 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | -| summaries.rb:115:26:115:32 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:115:26:115:32 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:54:6:54:25 | call to namedArg | summaries.rb:53:15:53:31 | call to source : | summaries.rb:54:6:54:25 | call to namedArg | $@ | summaries.rb:53:15:53:31 | call to source : | call to source : | +| summaries.rb:56:6:56:29 | call to anyArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:56:6:56:29 | call to anyArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:57:6:57:24 | call to anyArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:57:6:57:24 | call to anyArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:59:6:59:34 | call to anyNamedArg | summaries.rb:1:20:1:36 | call to source : | summaries.rb:59:6:59:34 | call to anyNamedArg | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:63:6:63:39 | call to anyPositionFromOne | summaries.rb:1:20:1:36 | call to source : | summaries.rb:63:6:63:39 | call to anyPositionFromOne | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:66:8:66:8 | x | summaries.rb:1:20:1:36 | call to source : | summaries.rb:66:8:66:8 | x | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:73:8:73:54 | call to preserveTaint | summaries.rb:73:24:73:53 | call to source : | summaries.rb:73:8:73:54 | call to preserveTaint | $@ | summaries.rb:73:24:73:53 | call to source : | call to source : | +| summaries.rb:76:8:76:57 | call to preserveTaint | summaries.rb:76:26:76:56 | call to source : | summaries.rb:76:8:76:57 | call to preserveTaint | $@ | summaries.rb:76:26:76:56 | call to source : | call to source : | +| summaries.rb:81:6:81:9 | ...[...] | summaries.rb:79:15:79:29 | call to source : | summaries.rb:81:6:81:9 | ...[...] | $@ | summaries.rb:79:15:79:29 | call to source : | call to source : | +| summaries.rb:81:6:81:9 | ...[...] | summaries.rb:79:15:79:29 | call to source : | summaries.rb:81:6:81:9 | ...[...] | $@ | summaries.rb:79:15:79:29 | call to source : | call to source : | +| summaries.rb:82:6:82:9 | ...[...] | summaries.rb:79:32:79:46 | call to source : | summaries.rb:82:6:82:9 | ...[...] | $@ | summaries.rb:79:32:79:46 | call to source : | call to source : | +| summaries.rb:82:6:82:9 | ...[...] | summaries.rb:79:32:79:46 | call to source : | summaries.rb:82:6:82:9 | ...[...] | $@ | summaries.rb:79:32:79:46 | call to source : | call to source : | +| summaries.rb:85:6:85:9 | ...[...] | summaries.rb:79:15:79:29 | call to source : | summaries.rb:85:6:85:9 | ...[...] | $@ | summaries.rb:79:15:79:29 | call to source : | call to source : | +| summaries.rb:85:6:85:9 | ...[...] | summaries.rb:79:15:79:29 | call to source : | summaries.rb:85:6:85:9 | ...[...] | $@ | summaries.rb:79:15:79:29 | call to source : | call to source : | +| summaries.rb:90:6:90:9 | ...[...] | summaries.rb:79:32:79:46 | call to source : | summaries.rb:90:6:90:9 | ...[...] | $@ | summaries.rb:79:32:79:46 | call to source : | call to source : | +| summaries.rb:90:6:90:9 | ...[...] | summaries.rb:79:32:79:46 | call to source : | summaries.rb:90:6:90:9 | ...[...] | $@ | summaries.rb:79:32:79:46 | call to source : | call to source : | +| summaries.rb:94:6:94:16 | call to get_value | summaries.rb:93:13:93:26 | call to source : | summaries.rb:94:6:94:16 | call to get_value | $@ | summaries.rb:93:13:93:26 | call to source : | call to source : | +| summaries.rb:94:6:94:16 | call to get_value | summaries.rb:93:13:93:26 | call to source : | summaries.rb:94:6:94:16 | call to get_value | $@ | summaries.rb:93:13:93:26 | call to source : | call to source : | +| summaries.rb:106:6:106:6 | y | summaries.rb:1:20:1:36 | call to source : | summaries.rb:106:6:106:6 | y | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:107:6:107:6 | z | summaries.rb:1:20:1:36 | call to source : | summaries.rb:107:6:107:6 | z | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:111:6:111:6 | x | summaries.rb:1:20:1:36 | call to source : | summaries.rb:111:6:111:6 | x | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:113:16:113:22 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:113:16:113:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:113:16:113:22 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:113:16:113:22 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:114:21:114:27 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:114:21:114:27 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:114:21:114:27 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:114:21:114:27 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:117:26:117:32 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:117:26:117:32 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | +| summaries.rb:117:26:117:32 | tainted | summaries.rb:1:20:1:36 | call to source : | summaries.rb:117:26:117:32 | tainted | $@ | summaries.rb:1:20:1:36 | call to source : | call to source : | warning | CSV type row should have 5 columns but has 2: test;TooFewColumns | | CSV type row should have 5 columns but has 8: test;TooManyColumns;;;Member[Foo].Instance;too;many;columns | diff --git a/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb b/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb index baa662736f5..cf92b7948e7 100644 --- a/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb +++ b/ruby/ql/test/library-tests/dataflow/summaries/summaries.rb @@ -50,6 +50,8 @@ end sink(Foo.namedArg(foo: tainted)) # $ hasTaintFlow=tainted sink(Foo.namedArg(tainted)) +args = { foo: source("tainted") } +sink(Foo.namedArg(**args)) # $ hasTaintFlow=tainted sink(Foo.anyArg(foo: tainted)) # $ hasTaintFlow=tainted sink(Foo.anyArg(tainted)) # $ hasTaintFlow=tainted diff --git a/ruby/ql/test/library-tests/frameworks/ActionController.expected b/ruby/ql/test/library-tests/frameworks/ActionController.expected index d306f09b64b..d6de776bbff 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionController.expected +++ b/ruby/ql/test/library-tests/frameworks/ActionController.expected @@ -1,17 +1,26 @@ actionControllerControllerClasses -| ActiveRecord.rb:23:1:39:3 | FooController | -| ActiveRecord.rb:41:1:64:3 | BarController | -| ActiveRecord.rb:66:1:70:3 | BazController | +| active_record/ActiveRecord.rb:23:1:39:3 | FooController | +| active_record/ActiveRecord.rb:41:1:64:3 | BarController | +| active_record/ActiveRecord.rb:66:1:94:3 | BazController | +| active_record/ActiveRecord.rb:96:1:104:3 | AnnotatedController | | app/controllers/comments_controller.rb:1:1:7:3 | CommentsController | | app/controllers/foo/bars_controller.rb:3:1:39:3 | BarsController | | app/controllers/photos_controller.rb:1:1:4:3 | PhotosController | | app/controllers/posts_controller.rb:1:1:10:3 | PostsController | | app/controllers/users/notifications_controller.rb:2:3:5:5 | NotificationsController | actionControllerActionMethods -| ActiveRecord.rb:27:3:38:5 | some_request_handler | -| ActiveRecord.rb:42:3:47:5 | some_other_request_handler | -| ActiveRecord.rb:49:3:63:5 | safe_paths | -| ActiveRecord.rb:67:3:69:5 | yet_another_handler | +| active_record/ActiveRecord.rb:27:3:38:5 | some_request_handler | +| active_record/ActiveRecord.rb:42:3:47:5 | some_other_request_handler | +| active_record/ActiveRecord.rb:49:3:63:5 | safe_paths | +| active_record/ActiveRecord.rb:67:3:69:5 | yet_another_handler | +| active_record/ActiveRecord.rb:71:3:73:5 | create1 | +| active_record/ActiveRecord.rb:75:3:77:5 | create2 | +| active_record/ActiveRecord.rb:79:3:81:5 | create3 | +| active_record/ActiveRecord.rb:83:3:85:5 | update1 | +| active_record/ActiveRecord.rb:87:3:89:5 | update2 | +| active_record/ActiveRecord.rb:91:3:93:5 | update3 | +| active_record/ActiveRecord.rb:97:3:99:5 | index | +| active_record/ActiveRecord.rb:101:3:103:5 | unsafe_action | | app/controllers/comments_controller.rb:2:3:3:5 | index | | app/controllers/comments_controller.rb:5:3:6:5 | show | | app/controllers/foo/bars_controller.rb:5:3:7:5 | index | @@ -25,38 +34,60 @@ actionControllerActionMethods | app/controllers/posts_controller.rb:8:3:9:5 | upvote | | app/controllers/users/notifications_controller.rb:3:5:4:7 | mark_as_read | paramsCalls -| ActiveRecord.rb:28:30:28:35 | call to params | -| ActiveRecord.rb:29:29:29:34 | call to params | -| ActiveRecord.rb:30:31:30:36 | call to params | -| ActiveRecord.rb:32:21:32:26 | call to params | -| ActiveRecord.rb:34:34:34:39 | call to params | -| ActiveRecord.rb:35:23:35:28 | call to params | -| ActiveRecord.rb:35:38:35:43 | call to params | -| ActiveRecord.rb:43:10:43:15 | call to params | -| ActiveRecord.rb:50:11:50:16 | call to params | -| ActiveRecord.rb:54:12:54:17 | call to params | -| ActiveRecord.rb:59:12:59:17 | call to params | -| ActiveRecord.rb:62:15:62:20 | call to params | -| ActiveRecord.rb:68:21:68:26 | call to params | +| active_record/ActiveRecord.rb:28:30:28:35 | call to params | +| active_record/ActiveRecord.rb:29:29:29:34 | call to params | +| active_record/ActiveRecord.rb:30:31:30:36 | call to params | +| active_record/ActiveRecord.rb:32:21:32:26 | call to params | +| active_record/ActiveRecord.rb:34:34:34:39 | call to params | +| active_record/ActiveRecord.rb:35:23:35:28 | call to params | +| active_record/ActiveRecord.rb:35:38:35:43 | call to params | +| active_record/ActiveRecord.rb:43:10:43:15 | call to params | +| active_record/ActiveRecord.rb:50:11:50:16 | call to params | +| active_record/ActiveRecord.rb:54:12:54:17 | call to params | +| active_record/ActiveRecord.rb:59:12:59:17 | call to params | +| active_record/ActiveRecord.rb:62:15:62:20 | call to params | +| active_record/ActiveRecord.rb:68:21:68:26 | call to params | +| active_record/ActiveRecord.rb:72:18:72:23 | call to params | +| active_record/ActiveRecord.rb:76:24:76:29 | call to params | +| active_record/ActiveRecord.rb:76:49:76:54 | call to params | +| active_record/ActiveRecord.rb:80:25:80:30 | call to params | +| active_record/ActiveRecord.rb:80:50:80:55 | call to params | +| active_record/ActiveRecord.rb:84:21:84:26 | call to params | +| active_record/ActiveRecord.rb:88:27:88:32 | call to params | +| active_record/ActiveRecord.rb:88:52:88:57 | call to params | +| active_record/ActiveRecord.rb:92:28:92:33 | call to params | +| active_record/ActiveRecord.rb:92:53:92:58 | call to params | +| active_record/ActiveRecord.rb:102:59:102:64 | call to params | | app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params | | app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params | | app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params | | app/controllers/foo/bars_controller.rb:22:10:22:15 | call to params | | app/views/foo/bars/show.html.erb:5:9:5:14 | call to params | paramsSources -| ActiveRecord.rb:28:30:28:35 | call to params | -| ActiveRecord.rb:29:29:29:34 | call to params | -| ActiveRecord.rb:30:31:30:36 | call to params | -| ActiveRecord.rb:32:21:32:26 | call to params | -| ActiveRecord.rb:34:34:34:39 | call to params | -| ActiveRecord.rb:35:23:35:28 | call to params | -| ActiveRecord.rb:35:38:35:43 | call to params | -| ActiveRecord.rb:43:10:43:15 | call to params | -| ActiveRecord.rb:50:11:50:16 | call to params | -| ActiveRecord.rb:54:12:54:17 | call to params | -| ActiveRecord.rb:59:12:59:17 | call to params | -| ActiveRecord.rb:62:15:62:20 | call to params | -| ActiveRecord.rb:68:21:68:26 | call to params | +| active_record/ActiveRecord.rb:28:30:28:35 | call to params | +| active_record/ActiveRecord.rb:29:29:29:34 | call to params | +| active_record/ActiveRecord.rb:30:31:30:36 | call to params | +| active_record/ActiveRecord.rb:32:21:32:26 | call to params | +| active_record/ActiveRecord.rb:34:34:34:39 | call to params | +| active_record/ActiveRecord.rb:35:23:35:28 | call to params | +| active_record/ActiveRecord.rb:35:38:35:43 | call to params | +| active_record/ActiveRecord.rb:43:10:43:15 | call to params | +| active_record/ActiveRecord.rb:50:11:50:16 | call to params | +| active_record/ActiveRecord.rb:54:12:54:17 | call to params | +| active_record/ActiveRecord.rb:59:12:59:17 | call to params | +| active_record/ActiveRecord.rb:62:15:62:20 | call to params | +| active_record/ActiveRecord.rb:68:21:68:26 | call to params | +| active_record/ActiveRecord.rb:72:18:72:23 | call to params | +| active_record/ActiveRecord.rb:76:24:76:29 | call to params | +| active_record/ActiveRecord.rb:76:49:76:54 | call to params | +| active_record/ActiveRecord.rb:80:25:80:30 | call to params | +| active_record/ActiveRecord.rb:80:50:80:55 | call to params | +| active_record/ActiveRecord.rb:84:21:84:26 | call to params | +| active_record/ActiveRecord.rb:88:27:88:32 | call to params | +| active_record/ActiveRecord.rb:88:52:88:57 | call to params | +| active_record/ActiveRecord.rb:92:28:92:33 | call to params | +| active_record/ActiveRecord.rb:92:53:92:58 | call to params | +| active_record/ActiveRecord.rb:102:59:102:64 | call to params | | app/controllers/foo/bars_controller.rb:13:21:13:26 | call to params | | app/controllers/foo/bars_controller.rb:14:10:14:15 | call to params | | app/controllers/foo/bars_controller.rb:21:21:21:26 | call to params | diff --git a/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected b/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected index 5ea30e67680..1593bb24374 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected +++ b/ruby/ql/test/library-tests/frameworks/ActionDispatch.expected @@ -54,3 +54,15 @@ underscore | HTTPServerRequest | httpserver_request | | LotsOfCapitalLetters | lots_of_capital_letters | | invalid | invalid | +mimeTypeInstances +| action_dispatch/mime_type.rb:2:6:2:28 | Use getMember("Mime").getMethod("fetch").getReturn() | +| action_dispatch/mime_type.rb:3:6:3:32 | Use getMember("Mime").getMember("Type").getMethod("new").getReturn() | +| action_dispatch/mime_type.rb:4:6:4:35 | Use getMember("Mime").getMember("Type").getMethod("lookup").getReturn() | +| action_dispatch/mime_type.rb:5:6:5:43 | Use getMember("Mime").getMember("Type").getMethod("lookup_by_extension").getReturn() | +| action_dispatch/mime_type.rb:6:6:6:47 | Use getMember("Mime").getMember("Type").getMethod("register").getReturn() | +| action_dispatch/mime_type.rb:7:6:7:64 | Use getMember("Mime").getMember("Type").getMethod("register_alias").getReturn() | +mimeTypeMatchRegExpInterpretations +| action_dispatch/mime_type.rb:11:11:11:19 | "foo/bar" | +| action_dispatch/mime_type.rb:12:7:12:15 | "foo/bar" | +| action_dispatch/mime_type.rb:13:11:13:11 | s | +| action_dispatch/mime_type.rb:14:7:14:7 | s | diff --git a/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql b/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql index a78d18629f0..f2f65f58523 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql +++ b/ruby/ql/test/library-tests/frameworks/ActionDispatch.ql @@ -1,9 +1,13 @@ private import ruby private import codeql.ruby.frameworks.ActionDispatch private import codeql.ruby.frameworks.ActionController +private import codeql.ruby.ApiGraphs +private import codeql.ruby.frameworks.data.ModelsAsData +private import codeql.ruby.DataFlow +private import codeql.ruby.Regexp as RE query predicate actionDispatchRoutes( - ActionDispatch::Route r, string method, string path, string controller, string action + ActionDispatch::Routing::Route r, string method, string path, string controller, string action ) { r.getHttpMethod() = method and r.getPath() = path and @@ -12,15 +16,25 @@ query predicate actionDispatchRoutes( } query predicate actionDispatchControllerMethods( - ActionDispatch::Route r, ActionControllerActionMethod m + ActionDispatch::Routing::Route r, ActionControllerActionMethod m ) { m.getARoute() = r } query predicate underscore(string input, string output) { - output = ActionDispatch::underscore(input) and + output = ActionDispatch::Routing::underscore(input) and input in [ "Foo", "FooBar", "Foo::Bar", "FooBar::Baz", "Foo::Bar::Baz", "Foo::Bar::BazQuux", "invalid", "HTTPServerRequest", "LotsOfCapitalLetters" ] } + +query predicate mimeTypeInstances(API::Node n) { + n = ModelOutput::getATypeNode("actiondispatch", "Mime::Type") +} + +query predicate mimeTypeMatchRegExpInterpretations( + ActionDispatch::MimeTypeMatchRegExpInterpretation s +) { + any() +} diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected deleted file mode 100644 index d8509f6a218..00000000000 --- a/ruby/ql/test/library-tests/frameworks/ActiveRecord.expected +++ /dev/null @@ -1,64 +0,0 @@ -activeRecordModelClasses -| ActiveRecord.rb:1:1:3:3 | UserGroup | -| ActiveRecord.rb:5:1:15:3 | User | -| ActiveRecord.rb:17:1:21:3 | Admin | -activeRecordInstances -| ActiveRecord.rb:9:5:9:68 | call to find | -| ActiveRecord.rb:13:5:13:40 | call to find_by | -| ActiveRecord.rb:36:5:36:30 | call to find_by_name | -| ActiveRecord.rb:55:5:57:7 | if ... | -| ActiveRecord.rb:55:43:56:40 | then ... | -| ActiveRecord.rb:56:7:56:40 | call to find_by | -| ActiveRecord.rb:60:5:60:33 | call to find_by | -| ActiveRecord.rb:62:5:62:34 | call to find | -activeRecordSqlExecutionRanges -| ActiveRecord.rb:9:33:9:67 | "name='#{...}' and pass='#{...}'" | -| ActiveRecord.rb:19:16:19:24 | condition | -| ActiveRecord.rb:28:30:28:44 | ...[...] | -| ActiveRecord.rb:29:20:29:42 | "id = '#{...}'" | -| ActiveRecord.rb:30:22:30:44 | "id = '#{...}'" | -| ActiveRecord.rb:31:16:31:21 | <<-SQL | -| ActiveRecord.rb:34:20:34:47 | "user.id = '#{...}'" | -| ActiveRecord.rb:46:20:46:32 | ... + ... | -| ActiveRecord.rb:52:16:52:28 | "name #{...}" | -| ActiveRecord.rb:56:20:56:39 | "username = #{...}" | -activeRecordModelClassMethodCalls -| ActiveRecord.rb:2:3:2:17 | call to has_many | -| ActiveRecord.rb:6:3:6:24 | call to belongs_to | -| ActiveRecord.rb:9:5:9:68 | call to find | -| ActiveRecord.rb:13:5:13:40 | call to find_by | -| ActiveRecord.rb:13:5:13:46 | call to users | -| ActiveRecord.rb:19:5:19:25 | call to destroy_by | -| ActiveRecord.rb:28:5:28:45 | call to calculate | -| ActiveRecord.rb:29:5:29:43 | call to delete_by | -| ActiveRecord.rb:30:5:30:46 | call to destroy_by | -| ActiveRecord.rb:31:5:31:35 | call to where | -| ActiveRecord.rb:34:5:34:14 | call to where | -| ActiveRecord.rb:34:5:34:48 | call to not | -| ActiveRecord.rb:35:5:35:51 | call to authenticate | -| ActiveRecord.rb:36:5:36:30 | call to find_by_name | -| ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method | -| ActiveRecord.rb:46:5:46:33 | call to delete_by | -| ActiveRecord.rb:52:5:52:29 | call to order | -| ActiveRecord.rb:56:7:56:40 | call to find_by | -| ActiveRecord.rb:60:5:60:33 | call to find_by | -| ActiveRecord.rb:62:5:62:34 | call to find | -| ActiveRecord.rb:68:5:68:45 | call to delete_by | -potentiallyUnsafeSqlExecutingMethodCall -| ActiveRecord.rb:9:5:9:68 | call to find | -| ActiveRecord.rb:19:5:19:25 | call to destroy_by | -| ActiveRecord.rb:28:5:28:45 | call to calculate | -| ActiveRecord.rb:29:5:29:43 | call to delete_by | -| ActiveRecord.rb:30:5:30:46 | call to destroy_by | -| ActiveRecord.rb:31:5:31:35 | call to where | -| ActiveRecord.rb:34:5:34:48 | call to not | -| ActiveRecord.rb:46:5:46:33 | call to delete_by | -| ActiveRecord.rb:52:5:52:29 | call to order | -| ActiveRecord.rb:56:7:56:40 | call to find_by | -activeRecordModelInstantiations -| ActiveRecord.rb:9:5:9:68 | call to find | ActiveRecord.rb:5:1:15:3 | User | -| ActiveRecord.rb:13:5:13:40 | call to find_by | ActiveRecord.rb:1:1:3:3 | UserGroup | -| ActiveRecord.rb:36:5:36:30 | call to find_by_name | ActiveRecord.rb:5:1:15:3 | User | -| ActiveRecord.rb:56:7:56:40 | call to find_by | ActiveRecord.rb:5:1:15:3 | User | -| ActiveRecord.rb:60:5:60:33 | call to find_by | ActiveRecord.rb:5:1:15:3 | User | -| ActiveRecord.rb:62:5:62:34 | call to find | ActiveRecord.rb:5:1:15:3 | User | diff --git a/ruby/ql/test/library-tests/frameworks/PosixSpawn.ql b/ruby/ql/test/library-tests/frameworks/PosixSpawn.ql index 12fb445cf15..994f0d162f0 100644 --- a/ruby/ql/test/library-tests/frameworks/PosixSpawn.ql +++ b/ruby/ql/test/library-tests/frameworks/PosixSpawn.ql @@ -5,11 +5,13 @@ import codeql.ruby.DataFlow query predicate systemCalls( PosixSpawn::SystemCall call, DataFlow::Node arg, boolean shellInterpreted ) { - arg = call.getAnArgument() and - if call.isShellInterpreted(arg) then shellInterpreted = true else shellInterpreted = false + call.isShellInterpreted(arg) and shellInterpreted = true + or + not call.isShellInterpreted(arg) and arg = call.getAnArgument() and shellInterpreted = false } query predicate childCalls(PosixSpawn::ChildCall call, DataFlow::Node arg, boolean shellInterpreted) { - arg = call.getAnArgument() and - if call.isShellInterpreted(arg) then shellInterpreted = true else shellInterpreted = false + call.isShellInterpreted(arg) and shellInterpreted = true + or + not call.isShellInterpreted(arg) and arg = call.getAnArgument() and shellInterpreted = false } diff --git a/ruby/ql/test/library-tests/frameworks/action_dispatch/mime_type.rb b/ruby/ql/test/library-tests/frameworks/action_dispatch/mime_type.rb new file mode 100644 index 00000000000..9f37682bb2b --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/action_dispatch/mime_type.rb @@ -0,0 +1,14 @@ +m1 = Mime["text/html"] # not recognised due to MaD limitation (can't parse method name) +m2 = Mime.fetch("text/html") +m3 = Mime::Type.new("text/html") +m4 = Mime::Type.lookup("text/html") +m5 = Mime::Type.lookup_by_extension("jpeg") +m6 = Mime::Type.register("text/calendar", :ics) +m7 = Mime::Type.register_alias("application/xml", :opf, %w(opf)) + +s = "foo/bar" + +m2.match? "foo/bar" +m3 =~ "foo/bar" +m4.match? s +m5 =~ s diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected new file mode 100644 index 00000000000..a77e70879ac --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected @@ -0,0 +1,220 @@ +activeRecordModelClasses +| ActiveRecord.rb:1:1:3:3 | UserGroup | +| ActiveRecord.rb:5:1:15:3 | User | +| ActiveRecord.rb:17:1:21:3 | Admin | +| associations.rb:1:1:3:3 | Author | +| associations.rb:5:1:9:3 | Post | +| associations.rb:11:1:13:3 | Tag | +| associations.rb:15:1:17:3 | Comment | +activeRecordInstances +| ActiveRecord.rb:9:5:9:68 | call to find | +| ActiveRecord.rb:13:5:13:40 | call to find_by | +| ActiveRecord.rb:13:5:13:46 | call to users | +| ActiveRecord.rb:36:5:36:30 | call to find_by_name | +| ActiveRecord.rb:55:5:57:7 | if ... | +| ActiveRecord.rb:55:43:56:40 | then ... | +| ActiveRecord.rb:56:7:56:40 | call to find_by | +| ActiveRecord.rb:60:5:60:33 | call to find_by | +| ActiveRecord.rb:62:5:62:34 | call to find | +| associations.rb:19:1:19:20 | ... = ... | +| associations.rb:19:1:19:20 | ... = ... | +| associations.rb:19:11:19:20 | call to new | +| associations.rb:21:1:21:28 | ... = ... | +| associations.rb:21:1:21:28 | ... = ... | +| associations.rb:21:9:21:15 | author1 | +| associations.rb:21:9:21:21 | call to posts | +| associations.rb:21:9:21:28 | call to create | +| associations.rb:23:1:23:32 | ... = ... | +| associations.rb:23:12:23:16 | post1 | +| associations.rb:23:12:23:25 | call to comments | +| associations.rb:23:12:23:32 | call to create | +| associations.rb:25:1:25:22 | ... = ... | +| associations.rb:25:1:25:22 | ... = ... | +| associations.rb:25:11:25:15 | post1 | +| associations.rb:25:11:25:22 | call to author | +| associations.rb:27:1:27:28 | ... = ... | +| associations.rb:27:1:27:28 | ... = ... | +| associations.rb:27:9:27:15 | author2 | +| associations.rb:27:9:27:21 | call to posts | +| associations.rb:27:9:27:28 | call to create | +| associations.rb:29:1:29:7 | author2 | +| associations.rb:29:1:29:13 | call to posts | +| associations.rb:29:1:29:22 | ... << ... | +| associations.rb:29:18:29:22 | post2 | +| associations.rb:31:1:31:5 | post1 | +| associations.rb:31:1:31:12 | __synth__0 | +| associations.rb:31:1:31:12 | call to author= | +| associations.rb:31:1:31:22 | ... | +| associations.rb:31:16:31:22 | ... = ... | +| associations.rb:31:16:31:22 | ... = ... | +| associations.rb:31:16:31:22 | author2 | +| associations.rb:35:1:35:5 | post2 | +| associations.rb:35:1:35:14 | call to comments | +| associations.rb:35:1:35:21 | call to create | +| associations.rb:37:1:37:7 | author1 | +| associations.rb:37:1:37:13 | call to posts | +| associations.rb:37:1:37:20 | call to reload | +| associations.rb:37:1:37:27 | call to create | +| associations.rb:39:1:39:5 | post1 | +| associations.rb:40:1:40:5 | post1 | +| associations.rb:42:1:42:7 | author1 | +| associations.rb:42:1:42:13 | call to posts | +| associations.rb:42:1:42:25 | call to push | +| associations.rb:42:20:42:24 | post2 | +| associations.rb:43:1:43:7 | author1 | +| associations.rb:43:1:43:13 | call to posts | +| associations.rb:43:1:43:27 | call to concat | +| associations.rb:43:22:43:26 | post2 | +| associations.rb:44:1:44:7 | author1 | +| associations.rb:44:1:44:13 | call to posts | +| associations.rb:44:1:44:19 | call to build | +| associations.rb:45:1:45:7 | author1 | +| associations.rb:45:1:45:13 | call to posts | +| associations.rb:45:1:45:20 | call to create | +| associations.rb:46:1:46:7 | author1 | +| associations.rb:46:1:46:13 | call to posts | +| associations.rb:46:1:46:21 | call to create! | +| associations.rb:47:1:47:7 | author1 | +| associations.rb:47:1:47:13 | call to posts | +| associations.rb:47:1:47:20 | call to delete | +| associations.rb:48:1:48:7 | author1 | +| associations.rb:48:1:48:13 | call to posts | +| associations.rb:48:1:48:24 | call to delete_all | +| associations.rb:49:1:49:7 | author1 | +| associations.rb:49:1:49:13 | call to posts | +| associations.rb:49:1:49:21 | call to destroy | +| associations.rb:50:1:50:7 | author1 | +| associations.rb:50:1:50:13 | call to posts | +| associations.rb:50:1:50:25 | call to destroy_all | +| associations.rb:51:1:51:7 | author1 | +| associations.rb:51:1:51:13 | call to posts | +| associations.rb:51:1:51:22 | call to distinct | +| associations.rb:51:1:51:36 | call to find | +| associations.rb:52:1:52:7 | author1 | +| associations.rb:52:1:52:13 | call to posts | +| associations.rb:52:1:52:19 | call to reset | +| associations.rb:52:1:52:33 | call to find | +| associations.rb:53:1:53:7 | author1 | +| associations.rb:53:1:53:13 | call to posts | +| associations.rb:53:1:53:20 | call to reload | +| associations.rb:53:1:53:34 | call to find | +activeRecordSqlExecutionRanges +| ActiveRecord.rb:9:33:9:67 | "name='#{...}' and pass='#{...}'" | +| ActiveRecord.rb:19:16:19:24 | condition | +| ActiveRecord.rb:28:30:28:44 | ...[...] | +| ActiveRecord.rb:29:20:29:42 | "id = '#{...}'" | +| ActiveRecord.rb:30:22:30:44 | "id = '#{...}'" | +| ActiveRecord.rb:31:16:31:21 | <<-SQL | +| ActiveRecord.rb:34:20:34:47 | "user.id = '#{...}'" | +| ActiveRecord.rb:46:20:46:32 | ... + ... | +| ActiveRecord.rb:52:16:52:28 | "name #{...}" | +| ActiveRecord.rb:56:20:56:39 | "username = #{...}" | +| ActiveRecord.rb:102:27:102:76 | "this is an unsafe annotation:..." | +activeRecordModelClassMethodCalls +| ActiveRecord.rb:2:3:2:17 | call to has_many | +| ActiveRecord.rb:6:3:6:24 | call to belongs_to | +| ActiveRecord.rb:9:5:9:68 | call to find | +| ActiveRecord.rb:13:5:13:40 | call to find_by | +| ActiveRecord.rb:13:5:13:46 | call to users | +| ActiveRecord.rb:19:5:19:25 | call to destroy_by | +| ActiveRecord.rb:28:5:28:45 | call to calculate | +| ActiveRecord.rb:29:5:29:43 | call to delete_by | +| ActiveRecord.rb:30:5:30:46 | call to destroy_by | +| ActiveRecord.rb:31:5:31:35 | call to where | +| ActiveRecord.rb:34:5:34:14 | call to where | +| ActiveRecord.rb:34:5:34:48 | call to not | +| ActiveRecord.rb:35:5:35:51 | call to authenticate | +| ActiveRecord.rb:36:5:36:30 | call to find_by_name | +| ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method | +| ActiveRecord.rb:46:5:46:33 | call to delete_by | +| ActiveRecord.rb:52:5:52:29 | call to order | +| ActiveRecord.rb:56:7:56:40 | call to find_by | +| ActiveRecord.rb:60:5:60:33 | call to find_by | +| ActiveRecord.rb:62:5:62:34 | call to find | +| ActiveRecord.rb:68:5:68:45 | call to delete_by | +| ActiveRecord.rb:72:5:72:24 | call to create | +| ActiveRecord.rb:76:5:76:66 | call to create | +| ActiveRecord.rb:80:5:80:68 | call to create | +| ActiveRecord.rb:84:5:84:27 | call to update | +| ActiveRecord.rb:88:5:88:69 | call to update | +| ActiveRecord.rb:92:5:92:71 | call to update | +| ActiveRecord.rb:98:13:98:54 | call to annotate | +| ActiveRecord.rb:102:13:102:77 | call to annotate | +| associations.rb:2:3:2:17 | call to has_many | +| associations.rb:6:3:6:20 | call to belongs_to | +| associations.rb:7:3:7:20 | call to has_many | +| associations.rb:8:3:8:31 | call to has_and_belongs_to_many | +| associations.rb:12:3:12:32 | call to has_and_belongs_to_many | +| associations.rb:16:3:16:18 | call to belongs_to | +| associations.rb:19:11:19:20 | call to new | +potentiallyUnsafeSqlExecutingMethodCall +| ActiveRecord.rb:9:5:9:68 | call to find | +| ActiveRecord.rb:19:5:19:25 | call to destroy_by | +| ActiveRecord.rb:28:5:28:45 | call to calculate | +| ActiveRecord.rb:29:5:29:43 | call to delete_by | +| ActiveRecord.rb:30:5:30:46 | call to destroy_by | +| ActiveRecord.rb:31:5:31:35 | call to where | +| ActiveRecord.rb:34:5:34:48 | call to not | +| ActiveRecord.rb:46:5:46:33 | call to delete_by | +| ActiveRecord.rb:52:5:52:29 | call to order | +| ActiveRecord.rb:56:7:56:40 | call to find_by | +| ActiveRecord.rb:102:13:102:77 | call to annotate | +activeRecordModelInstantiations +| ActiveRecord.rb:9:5:9:68 | call to find | ActiveRecord.rb:5:1:15:3 | User | +| ActiveRecord.rb:13:5:13:40 | call to find_by | ActiveRecord.rb:1:1:3:3 | UserGroup | +| ActiveRecord.rb:13:5:13:46 | call to users | ActiveRecord.rb:5:1:15:3 | User | +| ActiveRecord.rb:36:5:36:30 | call to find_by_name | ActiveRecord.rb:5:1:15:3 | User | +| ActiveRecord.rb:56:7:56:40 | call to find_by | ActiveRecord.rb:5:1:15:3 | User | +| ActiveRecord.rb:60:5:60:33 | call to find_by | ActiveRecord.rb:5:1:15:3 | User | +| ActiveRecord.rb:62:5:62:34 | call to find | ActiveRecord.rb:5:1:15:3 | User | +| associations.rb:19:11:19:20 | call to new | associations.rb:1:1:3:3 | Author | +| associations.rb:21:9:21:21 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:21:9:21:28 | call to create | associations.rb:5:1:9:3 | Post | +| associations.rb:23:12:23:25 | call to comments | associations.rb:15:1:17:3 | Comment | +| associations.rb:23:12:23:32 | call to create | associations.rb:15:1:17:3 | Comment | +| associations.rb:25:11:25:22 | call to author | associations.rb:1:1:3:3 | Author | +| associations.rb:27:9:27:21 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:27:9:27:28 | call to create | associations.rb:5:1:9:3 | Post | +| associations.rb:29:1:29:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:29:1:29:22 | ... << ... | associations.rb:11:1:13:3 | Tag | +| associations.rb:29:1:29:22 | ... << ... | associations.rb:15:1:17:3 | Comment | +| associations.rb:31:1:31:12 | call to author= | associations.rb:1:1:3:3 | Author | +| associations.rb:35:1:35:14 | call to comments | associations.rb:15:1:17:3 | Comment | +| associations.rb:35:1:35:21 | call to create | associations.rb:15:1:17:3 | Comment | +| associations.rb:37:1:37:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:37:1:37:20 | call to reload | associations.rb:5:1:9:3 | Post | +| associations.rb:42:1:42:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:42:1:42:25 | call to push | associations.rb:5:1:9:3 | Post | +| associations.rb:43:1:43:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:43:1:43:27 | call to concat | associations.rb:5:1:9:3 | Post | +| associations.rb:44:1:44:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:44:1:44:19 | call to build | associations.rb:5:1:9:3 | Post | +| associations.rb:45:1:45:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:45:1:45:20 | call to create | associations.rb:5:1:9:3 | Post | +| associations.rb:46:1:46:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:46:1:46:21 | call to create! | associations.rb:5:1:9:3 | Post | +| associations.rb:47:1:47:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:47:1:47:20 | call to delete | associations.rb:5:1:9:3 | Post | +| associations.rb:48:1:48:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:48:1:48:24 | call to delete_all | associations.rb:5:1:9:3 | Post | +| associations.rb:49:1:49:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:49:1:49:21 | call to destroy | associations.rb:5:1:9:3 | Post | +| associations.rb:50:1:50:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:50:1:50:25 | call to destroy_all | associations.rb:5:1:9:3 | Post | +| associations.rb:51:1:51:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:51:1:51:22 | call to distinct | associations.rb:5:1:9:3 | Post | +| associations.rb:52:1:52:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:52:1:52:19 | call to reset | associations.rb:5:1:9:3 | Post | +| associations.rb:53:1:53:13 | call to posts | associations.rb:5:1:9:3 | Post | +| associations.rb:53:1:53:20 | call to reload | associations.rb:5:1:9:3 | Post | +persistentWriteAccesses +| ActiveRecord.rb:72:5:72:24 | call to create | ActiveRecord.rb:72:18:72:23 | call to params | +| ActiveRecord.rb:76:5:76:66 | call to create | ActiveRecord.rb:76:24:76:36 | ...[...] | +| ActiveRecord.rb:76:5:76:66 | call to create | ActiveRecord.rb:76:49:76:65 | ...[...] | +| ActiveRecord.rb:80:5:80:68 | call to create | ActiveRecord.rb:80:25:80:37 | ...[...] | +| ActiveRecord.rb:80:5:80:68 | call to create | ActiveRecord.rb:80:50:80:66 | ...[...] | +| ActiveRecord.rb:84:5:84:27 | call to update | ActiveRecord.rb:84:21:84:26 | call to params | +| ActiveRecord.rb:88:5:88:69 | call to update | ActiveRecord.rb:88:27:88:39 | ...[...] | +| ActiveRecord.rb:88:5:88:69 | call to update | ActiveRecord.rb:88:52:88:68 | ...[...] | +| ActiveRecord.rb:92:5:92:71 | call to update | ActiveRecord.rb:92:21:92:70 | call to [] | +| associations.rb:31:16:31:22 | ... = ... | associations.rb:31:16:31:22 | author2 | diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecord.ql b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql similarity index 80% rename from ruby/ql/test/library-tests/frameworks/ActiveRecord.ql rename to ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql index 6d19f9bb9ec..731679e437b 100644 --- a/ruby/ql/test/library-tests/frameworks/ActiveRecord.ql +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql @@ -1,5 +1,7 @@ import codeql.ruby.controlflow.CfgNodes import codeql.ruby.frameworks.ActiveRecord +import codeql.ruby.Concepts +import codeql.ruby.DataFlow query predicate activeRecordModelClasses(ActiveRecordModelClass cls) { any() } @@ -18,3 +20,7 @@ query predicate activeRecordModelInstantiations( ) { i.getClass() = cls } + +query predicate persistentWriteAccesses(PersistentWriteAccess w, DataFlow::Node value) { + w.getValue() = value +} diff --git a/ruby/ql/test/library-tests/frameworks/ActiveRecord.rb b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb similarity index 69% rename from ruby/ql/test/library-tests/frameworks/ActiveRecord.rb rename to ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb index 5045a5c2264..46df51deb43 100644 --- a/ruby/ql/test/library-tests/frameworks/ActiveRecord.rb +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.rb @@ -67,4 +67,38 @@ class BazController < BarController def yet_another_handler Admin.delete_by(params[:admin_condition]) end + + def create1 + Admin.create(params) + end + + def create2 + Admin.create(name: params[:name], password: params[:password]) + end + + def create3 + Admin.create({name: params[:name], password: params[:password]}) + end + + def update1 + Admin.update(1, params) + end + + def update2 + Admin.update(1, name: params[:name], password: params[:password]) + end + + def update3 + Admin.update(1, {name: params[:name], password: params[:password]}) + end +end + +class AnnotatedController < ActionController::Base + def index + users = User.annotate("this is a safe annotation") + end + + def unsafe_action + users = User.annotate("this is an unsafe annotation:#{params[:comment]}") + end end diff --git a/ruby/ql/test/library-tests/frameworks/active_record/associations.rb b/ruby/ql/test/library-tests/frameworks/active_record/associations.rb new file mode 100644 index 00000000000..891ee45fb72 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/active_record/associations.rb @@ -0,0 +1,53 @@ +class Author < ActiveRecord::Base + has_many :posts +end + +class Post < ActiveRecord::Base + belongs_to :author + has_many :comments + has_and_belongs_to_many :tags +end + +class Tag < ActiveRecord::Base + has_and_belongs_to_many :posts +end + +class Comment < ActiveRecord::Base + belongs_to :post +end + +author1 = Author.new + +post1 = author1.posts.create + +comment1 = post1.comments.create + +author2 = post1.author + +post2 = author2.posts.create + +author2.posts << post2 + +post1.author = author2 + +# The final method call in this chain should not be recognised as an +# instantiation. +post2.comments.create.create + +author1.posts.reload.create + +post1.build_tag +post1.build_tag + +author1.posts.push(post2) +author1.posts.concat(post2) +author1.posts.build +author1.posts.create +author1.posts.create! +author1.posts.delete +author1.posts.delete_all +author1.posts.destroy +author1.posts.destroy_all +author1.posts.distinct.find(post_id) +author1.posts.reset.find(post_id) +author1.posts.reload.find(post_id) diff --git a/ruby/ql/test/library-tests/frameworks/arel/Arel.expected b/ruby/ql/test/library-tests/frameworks/arel/Arel.expected new file mode 100644 index 00000000000..63cd556e836 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/arel/Arel.expected @@ -0,0 +1,3 @@ +failures +#select +| arel.rb:3:8:3:18 | call to sql | arel.rb:2:7:2:14 | call to source : | arel.rb:3:8:3:18 | call to sql | $@ | arel.rb:2:7:2:14 | call to source : | call to source : | diff --git a/ruby/ql/test/library-tests/frameworks/arel/Arel.ql b/ruby/ql/test/library-tests/frameworks/arel/Arel.ql new file mode 100644 index 00000000000..197a710803e --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/arel/Arel.ql @@ -0,0 +1,11 @@ +/** + * @kind path-problem + */ + +import codeql.ruby.frameworks.Arel +import ruby +import TestUtilities.InlineFlowTest + +from DataFlow::PathNode source, DataFlow::PathNode sink, DefaultTaintFlowConf conf +where conf.hasFlowPath(source, sink) +select sink, source, sink, "$@", source, source.toString() diff --git a/ruby/ql/test/library-tests/frameworks/arel/arel.rb b/ruby/ql/test/library-tests/frameworks/arel/arel.rb new file mode 100644 index 00000000000..7e7bc51f9a8 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/arel/arel.rb @@ -0,0 +1,14 @@ +def m1 + x = source 1 + sink(Arel.sql(x)) # $hasTaintFlow=1 +end + +def m2 + x = 1 + sink(Arel.sql(x)) +end + +def m3 + x = source 1 + sink(Unrelated.method(x)) +end diff --git a/ruby/ql/test/library-tests/frameworks/pathname/Pathname.expected b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.expected new file mode 100644 index 00000000000..2550c5b4f0d --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.expected @@ -0,0 +1,134 @@ +pathnameInstances +| Pathname.rb:2:1:2:33 | ... = ... | +| Pathname.rb:2:1:2:33 | ... = ... | +| Pathname.rb:2:12:2:33 | call to new | +| Pathname.rb:3:1:3:20 | ... = ... | +| Pathname.rb:3:13:3:20 | foo_path | +| Pathname.rb:4:1:4:8 | foo_path | +| Pathname.rb:6:1:6:29 | ... = ... | +| Pathname.rb:6:1:6:29 | ... = ... | +| Pathname.rb:6:12:6:29 | call to new | +| Pathname.rb:9:1:9:21 | ... = ... | +| Pathname.rb:9:1:9:21 | ... = ... | +| Pathname.rb:9:8:9:21 | call to getwd | +| Pathname.rb:10:1:10:21 | ... = ... | +| Pathname.rb:10:7:10:10 | pwd1 | +| Pathname.rb:10:7:10:21 | ... + ... | +| Pathname.rb:10:14:10:21 | foo_path | +| Pathname.rb:11:1:11:21 | ... = ... | +| Pathname.rb:11:1:11:21 | ... = ... | +| Pathname.rb:11:7:11:10 | pwd1 | +| Pathname.rb:11:7:11:21 | ... / ... | +| Pathname.rb:11:14:11:21 | bar_path | +| Pathname.rb:12:1:12:19 | ... = ... | +| Pathname.rb:12:7:12:10 | pwd1 | +| Pathname.rb:12:7:12:19 | call to basename | +| Pathname.rb:13:1:13:46 | ... = ... | +| Pathname.rb:13:7:13:36 | call to new | +| Pathname.rb:13:7:13:46 | call to cleanpath | +| Pathname.rb:14:1:14:26 | ... = ... | +| Pathname.rb:14:7:14:14 | foo_path | +| Pathname.rb:14:7:14:26 | call to expand_path | +| Pathname.rb:15:1:15:39 | ... = ... | +| Pathname.rb:15:7:15:10 | pwd1 | +| Pathname.rb:15:7:15:39 | call to join | +| Pathname.rb:16:1:16:23 | ... = ... | +| Pathname.rb:16:7:16:14 | foo_path | +| Pathname.rb:16:7:16:23 | call to realpath | +| Pathname.rb:17:1:17:59 | ... = ... | +| Pathname.rb:17:7:17:33 | call to new | +| Pathname.rb:17:7:17:59 | call to relative_path_from | +| Pathname.rb:18:1:18:33 | ... = ... | +| Pathname.rb:18:1:18:33 | ... = ... | +| Pathname.rb:18:7:18:10 | pwd1 | +| Pathname.rb:18:7:18:33 | call to sub | +| Pathname.rb:19:1:19:29 | ... = ... | +| Pathname.rb:19:7:19:14 | foo_path | +| Pathname.rb:19:7:19:29 | call to sub_ext | +| Pathname.rb:20:1:20:22 | ... = ... | +| Pathname.rb:20:7:20:14 | foo_path | +| Pathname.rb:20:7:20:22 | call to to_path | +| Pathname.rb:23:14:23:21 | foo_path | +| Pathname.rb:26:12:26:19 | foo_path | +| Pathname.rb:28:11:28:14 | pwd1 | +| Pathname.rb:32:12:32:19 | foo_path | +| Pathname.rb:35:1:35:8 | foo_path | +| Pathname.rb:38:1:38:8 | foo_path | +| Pathname.rb:39:12:39:19 | foo_path | +| Pathname.rb:41:1:41:3 | p08 | +| Pathname.rb:42:1:42:3 | p01 | +fileSystemAccesses +| Pathname.rb:26:12:26:24 | call to open | Pathname.rb:26:12:26:19 | foo_path | +| Pathname.rb:28:11:28:22 | call to opendir | Pathname.rb:28:11:28:14 | pwd1 | +| Pathname.rb:32:12:32:24 | call to read | Pathname.rb:32:12:32:19 | foo_path | +| Pathname.rb:35:1:35:23 | call to write | Pathname.rb:35:1:35:8 | foo_path | +| Pathname.rb:39:12:39:34 | call to open | Pathname.rb:39:12:39:19 | foo_path | +fileNameSources +| Pathname.rb:2:1:2:33 | ... = ... | +| Pathname.rb:2:1:2:33 | ... = ... | +| Pathname.rb:2:12:2:33 | call to new | +| Pathname.rb:3:1:3:20 | ... = ... | +| Pathname.rb:3:13:3:20 | foo_path | +| Pathname.rb:4:1:4:8 | foo_path | +| Pathname.rb:6:1:6:29 | ... = ... | +| Pathname.rb:6:1:6:29 | ... = ... | +| Pathname.rb:6:12:6:29 | call to new | +| Pathname.rb:9:1:9:21 | ... = ... | +| Pathname.rb:9:1:9:21 | ... = ... | +| Pathname.rb:9:8:9:21 | call to getwd | +| Pathname.rb:10:1:10:21 | ... = ... | +| Pathname.rb:10:7:10:10 | pwd1 | +| Pathname.rb:10:7:10:21 | ... + ... | +| Pathname.rb:10:14:10:21 | foo_path | +| Pathname.rb:11:1:11:21 | ... = ... | +| Pathname.rb:11:1:11:21 | ... = ... | +| Pathname.rb:11:7:11:10 | pwd1 | +| Pathname.rb:11:7:11:21 | ... / ... | +| Pathname.rb:11:14:11:21 | bar_path | +| Pathname.rb:12:1:12:19 | ... = ... | +| Pathname.rb:12:7:12:10 | pwd1 | +| Pathname.rb:12:7:12:19 | call to basename | +| Pathname.rb:13:1:13:46 | ... = ... | +| Pathname.rb:13:7:13:36 | call to new | +| Pathname.rb:13:7:13:46 | call to cleanpath | +| Pathname.rb:14:1:14:26 | ... = ... | +| Pathname.rb:14:7:14:14 | foo_path | +| Pathname.rb:14:7:14:26 | call to expand_path | +| Pathname.rb:15:1:15:39 | ... = ... | +| Pathname.rb:15:7:15:10 | pwd1 | +| Pathname.rb:15:7:15:39 | call to join | +| Pathname.rb:16:1:16:23 | ... = ... | +| Pathname.rb:16:7:16:14 | foo_path | +| Pathname.rb:16:7:16:23 | call to realpath | +| Pathname.rb:17:1:17:59 | ... = ... | +| Pathname.rb:17:7:17:33 | call to new | +| Pathname.rb:17:7:17:59 | call to relative_path_from | +| Pathname.rb:18:1:18:33 | ... = ... | +| Pathname.rb:18:1:18:33 | ... = ... | +| Pathname.rb:18:7:18:10 | pwd1 | +| Pathname.rb:18:7:18:33 | call to sub | +| Pathname.rb:19:1:19:29 | ... = ... | +| Pathname.rb:19:7:19:14 | foo_path | +| Pathname.rb:19:7:19:29 | call to sub_ext | +| Pathname.rb:20:1:20:22 | ... = ... | +| Pathname.rb:20:7:20:14 | foo_path | +| Pathname.rb:20:7:20:22 | call to to_path | +| Pathname.rb:23:14:23:21 | foo_path | +| Pathname.rb:23:14:23:26 | call to to_s | +| Pathname.rb:26:12:26:19 | foo_path | +| Pathname.rb:28:11:28:14 | pwd1 | +| Pathname.rb:32:12:32:19 | foo_path | +| Pathname.rb:35:1:35:8 | foo_path | +| Pathname.rb:38:1:38:8 | foo_path | +| Pathname.rb:39:12:39:19 | foo_path | +| Pathname.rb:41:1:41:3 | p08 | +| Pathname.rb:42:1:42:3 | p01 | +fileSystemReadAccesses +| Pathname.rb:32:12:32:24 | call to read | Pathname.rb:32:12:32:24 | call to read | +fileSystemWriteAccesses +| Pathname.rb:35:1:35:23 | call to write | Pathname.rb:35:16:35:23 | "output" | +fileSystemPermissionModifications +| Pathname.rb:38:1:38:19 | call to chmod | Pathname.rb:38:16:38:19 | 0644 | +| Pathname.rb:39:12:39:34 | call to open | Pathname.rb:39:31:39:34 | 0666 | +| Pathname.rb:41:1:41:14 | call to mkdir | Pathname.rb:41:11:41:14 | 0755 | +| Pathname.rb:42:1:42:22 | call to mkpath | Pathname.rb:42:18:42:21 | 0644 | diff --git a/ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql new file mode 100644 index 00000000000..71150ae5376 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.ql @@ -0,0 +1,26 @@ +private import ruby +private import codeql.ruby.Concepts +private import codeql.ruby.DataFlow +private import codeql.ruby.frameworks.stdlib.Pathname + +query predicate pathnameInstances(Pathname::PathnameInstance i) { any() } + +query predicate fileSystemAccesses(FileSystemAccess a, DataFlow::Node p) { + p = a.getAPathArgument() +} + +query predicate fileNameSources(FileNameSource s) { any() } + +query predicate fileSystemReadAccesses(FileSystemReadAccess a, DataFlow::Node d) { + d = a.getADataNode() +} + +query predicate fileSystemWriteAccesses(FileSystemWriteAccess a, DataFlow::Node d) { + d = a.getADataNode() +} + +query predicate fileSystemPermissionModifications( + FileSystemPermissionModification m, DataFlow::Node p +) { + p = m.getAPermissionNode() +} diff --git a/ruby/ql/test/library-tests/frameworks/pathname/Pathname.rb b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.rb new file mode 100644 index 00000000000..ea8f9812281 --- /dev/null +++ b/ruby/ql/test/library-tests/frameworks/pathname/Pathname.rb @@ -0,0 +1,42 @@ + +foo_path = Pathname.new "foo.txt" +foo_path2 = foo_path +foo_path + +bar_path = Pathname.new 'bar' + +# All these calls return new `Pathname` instances +pwd1 = Pathname.getwd +p00 = pwd1 + foo_path +p01 = pwd1 / bar_path +p02 = pwd1.basename +p03 = Pathname.new('bar/../baz.txt').cleanpath +p04 = foo_path.expand_path +p05 = pwd1.join 'bar', 'baz', 'qux.txt' +p06 = foo_path.realpath +p07 = Pathname.new('foo/bar.txt').relative_path_from('foo') +p08 = pwd1.sub 'wibble', 'wobble' +p09 = foo_path.sub_ext '.log' +p10 = foo_path.to_path + +# `Pathname#to_s` returns a string that we consider to be a filename source. +foo_string = foo_path.to_s + +# File-system accesses +foo_file = foo_path.open +foo_file.close +pwd_dir = pwd1.opendir +pwd_dir.close + +# Read from a file +foo_data = foo_path.read + +# Write to a file +foo_path.write 'output' + +# Permission modifications +foo_path.chmod 0644 +foo_file = foo_path.open 'w', 0666 +foo_file.close +p08.mkdir 0755 +p01.mkpath(mode: 0644) diff --git a/ruby/ql/test/library-tests/frameworks/railties/Railties.expected b/ruby/ql/test/library-tests/frameworks/railties/Railties.expected index c012090bbe1..42cac29b7c5 100644 --- a/ruby/ql/test/library-tests/frameworks/railties/Railties.expected +++ b/ruby/ql/test/library-tests/frameworks/railties/Railties.expected @@ -1,5 +1,14 @@ +systemCommandExecutions | Railties.rb:5:5:5:34 | call to execute_command | | Railties.rb:6:5:6:37 | call to execute_command | | Railties.rb:8:5:8:16 | call to rake | | Railties.rb:10:5:10:27 | call to rails_command | | Railties.rb:12:5:12:17 | call to git | +shellInterpretedArguments +| Railties.rb:5:5:5:34 | call to execute_command | Railties.rb:5:21:5:25 | :rake | +| Railties.rb:5:5:5:34 | call to execute_command | Railties.rb:5:28:5:33 | "test" | +| Railties.rb:6:5:6:37 | call to execute_command | Railties.rb:6:21:6:26 | :rails | +| Railties.rb:6:5:6:37 | call to execute_command | Railties.rb:6:29:6:36 | "server" | +| Railties.rb:8:5:8:16 | call to rake | Railties.rb:8:10:8:15 | "test" | +| Railties.rb:10:5:10:27 | call to rails_command | Railties.rb:10:19:10:26 | "server" | +| Railties.rb:12:5:12:17 | call to git | Railties.rb:12:9:12:16 | "status" | diff --git a/ruby/ql/test/library-tests/frameworks/railties/Railties.ql b/ruby/ql/test/library-tests/frameworks/railties/Railties.ql index 9a9731befb4..4dee50abcd5 100644 --- a/ruby/ql/test/library-tests/frameworks/railties/Railties.ql +++ b/ruby/ql/test/library-tests/frameworks/railties/Railties.ql @@ -1,5 +1,10 @@ private import ruby private import codeql.ruby.Concepts private import codeql.ruby.frameworks.Railties +private import codeql.ruby.DataFlow query predicate systemCommandExecutions(SystemCommandExecution e) { any() } + +query predicate shellInterpretedArguments(SystemCommandExecution e, DataFlow::Node arg) { + e.isShellInterpreted(arg) +} diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected new file mode 100644 index 00000000000..1fdf8045c23 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.expected @@ -0,0 +1,32 @@ +edges +| ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:11:14:11:42 | ...[...] : | +| ManuallyCheckHttpVerb.rb:11:14:11:42 | ...[...] : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | +| ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | +| ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | +| ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | +| ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | +| ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | +nodes +| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | semmle.label | call to get? | +| ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | semmle.label | call to env : | +| ManuallyCheckHttpVerb.rb:11:14:11:42 | ...[...] : | semmle.label | ...[...] : | +| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | semmle.label | ... == ... | +| ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | semmle.label | call to request_method : | +| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | semmle.label | ... == ... | +| ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | semmle.label | call to method : | +| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | semmle.label | ... == ... | +| ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | semmle.label | call to raw_request_method : | +| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | semmle.label | ... == ... | +| ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | semmle.label | call to request_method_symbol : | +| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | semmle.label | ... == ... | +| ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | semmle.label | call to env : | +| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | semmle.label | ...[...] | +subpaths +#select +| ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | ManuallyCheckHttpVerb.rb:4:8:4:19 | call to get? | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | ManuallyCheckHttpVerb.rb:11:14:11:24 | call to env : | ManuallyCheckHttpVerb.rb:12:8:12:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | ManuallyCheckHttpVerb.rb:19:14:19:35 | call to request_method : | ManuallyCheckHttpVerb.rb:20:8:20:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | ManuallyCheckHttpVerb.rb:27:14:27:27 | call to method : | ManuallyCheckHttpVerb.rb:28:8:28:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | ManuallyCheckHttpVerb.rb:35:14:35:39 | call to raw_request_method : | ManuallyCheckHttpVerb.rb:36:8:36:22 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | ManuallyCheckHttpVerb.rb:51:16:51:44 | call to request_method_symbol : | ManuallyCheckHttpVerb.rb:52:10:52:23 | ... == ... | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | +| ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | ManuallyCheckHttpVerb.rb:59:10:59:20 | call to env : | ManuallyCheckHttpVerb.rb:59:10:59:38 | ...[...] | Manually checking HTTP verbs is an indication that multiple requests are routed to the same controller action. This could lead to bypassing necessary authorization methods and other protections, like CSRF protection. Prefer using different controller actions for each HTTP method and relying Rails routing to handle mappting resources and verbs to specific methods. | diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qlref b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qlref new file mode 100644 index 00000000000..463c21cd0f2 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.qlref @@ -0,0 +1 @@ +experimental/manually-check-http-verb/ManuallyCheckHttpVerb.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb new file mode 100644 index 00000000000..055e9d98638 --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/manually-check-http-verb/ManuallyCheckHttpVerb.rb @@ -0,0 +1,117 @@ +class ExampleController < ActionController::Base + # Should find + def example_action + if request.get? + Resource.find(id: params[:example_id]) + end + end + + # Should find + def other_action + method = request.env['REQUEST_METHOD'] + if method == "GET" + Resource.find(id: params[:id]) + end + end + + # Should find + def foo + method = request.request_method + if method == "GET" + Resource.find(id: params[:id]) + end + end + + # Should find + def bar + method = request.method + if method == "GET" + Resource.find(id: params[:id]) + end + end + + # Should find + def baz + method = request.raw_request_method + if method == "GET" + Resource.find(id: params[:id]) + end + end + + # Should not find + def baz2 + method = request.raw_request_method + if some_other_function == "GET" + Resource.find(id: params[:id]) + end + end + + # Should find + def foobarbaz + method = request.request_method_symbol + if method == :GET + Resource.find(id: params[:id]) + end + end + + # Should find + def resource_action + case request.env['REQUEST_METHOD'] + when "GET" + Resource.find(id: params[:id]) + when "POST" + Resource.new(id: params[:id], details: params[:details]) + end + end + + # Should not find + def resource_action + case request.random_method + when "GET" + Resource.find(id: params[:id]) + when "POST" + Resource.new(id: params[:id], details: params[:details]) + end + end +end + +class SafeController < ActionController::Base + # this class should have no hits because controllers rely on conventional Rails routes + def index + Resource.find(id: params[:id]) + end + + def create + Resource.new(id: params[:id], details: params[:details]) + end + + def update + Resource.update(id: params[:id], details: params[:details]) + end + + def delete + s = Resource.find(id: params[:id]) + s.delete + end +end + +# There should be no hits from this class because it does not inherit from ActionController +class NotAController + def example_action + if request.get? + Resource.find(params[:example_id]) + end + end + + def resource_action + case env['REQUEST_METHOD'] + when "GET" + Resource.find(params[:id]) + when "POST" + Resource.new(params[:id], params[:details]) + end + end +end + +class Resource < ActiveRecord::Base +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.expected b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.expected new file mode 100644 index 00000000000..14bd3e4e13f --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.expected @@ -0,0 +1,20 @@ +edges +| WeakParams.rb:5:28:5:53 | call to request_parameters : | WeakParams.rb:5:28:5:59 | ...[...] | +| WeakParams.rb:10:28:10:51 | call to query_parameters : | WeakParams.rb:10:28:10:57 | ...[...] | +| WeakParams.rb:15:28:15:39 | call to POST : | WeakParams.rb:15:28:15:45 | ...[...] | +| WeakParams.rb:20:28:20:38 | call to GET : | WeakParams.rb:20:28:20:44 | ...[...] | +nodes +| WeakParams.rb:5:28:5:53 | call to request_parameters : | semmle.label | call to request_parameters : | +| WeakParams.rb:5:28:5:59 | ...[...] | semmle.label | ...[...] | +| WeakParams.rb:10:28:10:51 | call to query_parameters : | semmle.label | call to query_parameters : | +| WeakParams.rb:10:28:10:57 | ...[...] | semmle.label | ...[...] | +| WeakParams.rb:15:28:15:39 | call to POST : | semmle.label | call to POST : | +| WeakParams.rb:15:28:15:45 | ...[...] | semmle.label | ...[...] | +| WeakParams.rb:20:28:20:38 | call to GET : | semmle.label | call to GET : | +| WeakParams.rb:20:28:20:44 | ...[...] | semmle.label | ...[...] | +subpaths +#select +| WeakParams.rb:5:28:5:59 | ...[...] | WeakParams.rb:5:28:5:53 | call to request_parameters : | WeakParams.rb:5:28:5:59 | ...[...] | By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html | +| WeakParams.rb:10:28:10:57 | ...[...] | WeakParams.rb:10:28:10:51 | call to query_parameters : | WeakParams.rb:10:28:10:57 | ...[...] | By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html | +| WeakParams.rb:15:28:15:45 | ...[...] | WeakParams.rb:15:28:15:39 | call to POST : | WeakParams.rb:15:28:15:45 | ...[...] | By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html | +| WeakParams.rb:20:28:20:44 | ...[...] | WeakParams.rb:20:28:20:38 | call to GET : | WeakParams.rb:20:28:20:44 | ...[...] | By exposing all keys in request parameters or by blindy accessing them, unintended parameters could be used and lead to mass-assignment or have other unexpected side-effects. It is safer to follow the 'strong parameters' pattern in Rails, which is outlined here: https://api.rubyonrails.org/classes/ActionController/StrongParameters.html | diff --git a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.qlref b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.qlref new file mode 100644 index 00000000000..5350e4bf40a --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.qlref @@ -0,0 +1 @@ +experimental/weak-params/WeakParams.ql \ No newline at end of file diff --git a/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb new file mode 100644 index 00000000000..a5edef2e6dc --- /dev/null +++ b/ruby/ql/test/query-tests/experimental/weak-params/WeakParams.rb @@ -0,0 +1,40 @@ +class TestController < ActionController::Base + + # Should catch + def create + TestObject.create(foo: request.request_parameters[:foo]) + end + + # Should catch + def create_query + TestObject.create(foo: request.query_parameters[:foo]) + end + + # Should catch + def update_unsafe + TestObject.update(foo: request.POST[:foo]) + end + + # Should catch + def update_unsafe_get + TestObject.update(foo: request.GET[:foo]) + end + + # Should not catch + def update + TestObject.update(object_params) + end + + # strong params method + def object_params + params.require(:uuid).permit(:notes) + end + + # Should not catch + def test_non_sink + puts request.request_parameters + end +end + +class TestObject < ActiveRecord::Base +end \ No newline at end of file diff --git a/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb b/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb index 9c314a82b34..4c8dfcca10d 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb +++ b/ruby/ql/test/query-tests/security/cwe-089/ActiveRecordInjection.rb @@ -137,3 +137,17 @@ class BazController < BarController Admin.delete_by(params[:admin_condition]) end end + +class AnnotatedController < ActionController::Base + def index + name = params[:user_name] + # GOOD: string literal arguments not controlled by user are safe for annotations + users = User.annotate("this is a safe annotation").find_by(user_name: name) + end + + def unsafe_action + name = params[:user_name] + # BAD: user input passed into annotations are vulnerable to SQLi + users = User.annotate("this is an unsafe annotation:#{params[:comment]}").find_by(user_name: name) + end +end diff --git a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected index 6a9f5f771fb..dc814977cae 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected +++ b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected @@ -31,6 +31,8 @@ edges | ActiveRecordInjection.rb:99:11:99:17 | ...[...] : | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | | ActiveRecordInjection.rb:137:21:137:26 | call to params : | ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | | ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | ActiveRecordInjection.rb:20:22:20:30 | condition : | +| ActiveRecordInjection.rb:151:59:151:64 | call to params : | ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | +| ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | nodes | ActiveRecordInjection.rb:8:25:8:28 | name : | semmle.label | name : | | ActiveRecordInjection.rb:8:31:8:34 | pass : | semmle.label | pass : | @@ -80,6 +82,9 @@ nodes | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | semmle.label | ... + ... | | ActiveRecordInjection.rb:137:21:137:26 | call to params : | semmle.label | call to params : | | ActiveRecordInjection.rb:137:21:137:44 | ...[...] : | semmle.label | ...[...] : | +| ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | semmle.label | "this is an unsafe annotation:..." | +| ActiveRecordInjection.rb:151:59:151:64 | call to params : | semmle.label | call to params : | +| ActiveRecordInjection.rb:151:59:151:74 | ...[...] : | semmle.label | ...[...] : | subpaths #select | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | ActiveRecordInjection.rb:70:23:70:28 | call to params : | ActiveRecordInjection.rb:10:33:10:67 | "name='#{...}' and pass='#{...}'" | This SQL query depends on $@. | ActiveRecordInjection.rb:70:23:70:28 | call to params | a user-provided value | @@ -99,3 +104,4 @@ subpaths | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | ActiveRecordInjection.rb:88:18:88:23 | call to params : | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:88:18:88:23 | call to params | a user-provided value | | ActiveRecordInjection.rb:92:21:92:35 | ...[...] | ActiveRecordInjection.rb:92:21:92:26 | call to params : | ActiveRecordInjection.rb:92:21:92:35 | ...[...] | This SQL query depends on $@. | ActiveRecordInjection.rb:92:21:92:26 | call to params | a user-provided value | | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | ActiveRecordInjection.rb:98:10:98:15 | call to params : | ActiveRecordInjection.rb:104:20:104:32 | ... + ... | This SQL query depends on $@. | ActiveRecordInjection.rb:98:10:98:15 | call to params | a user-provided value | +| ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | ActiveRecordInjection.rb:151:59:151:64 | call to params : | ActiveRecordInjection.rb:151:27:151:76 | "this is an unsafe annotation:..." | This SQL query depends on $@. | ActiveRecordInjection.rb:151:59:151:64 | call to params | a user-provided value | diff --git a/swift/codegen/generators/cppgen.py b/swift/codegen/generators/cppgen.py index ef042f42dbd..420e82605e1 100644 --- a/swift/codegen/generators/cppgen.py +++ b/swift/codegen/generators/cppgen.py @@ -65,7 +65,7 @@ class Processor: return cpp.Class( name=name, bases=[self._get_class(b) for b in cls.bases], - fields=[_get_field(cls, p) for p in cls.properties], + fields=[_get_field(cls, p) for p in cls.properties if "cpp_skip" not in p.pragmas], final=not cls.derived, trap_name=trap_name, ) diff --git a/swift/codegen/generators/trapgen.py b/swift/codegen/generators/trapgen.py index eb701b629ef..22c46e6e146 100755 --- a/swift/codegen/generators/trapgen.py +++ b/swift/codegen/generators/trapgen.py @@ -86,11 +86,10 @@ def generate(opts, renderer): renderer.render(cpp.TrapList(entries, opts.dbscheme), out / dir / "TrapEntries") tags = [] - for index, tag in enumerate(toposort_flatten(tag_graph)): + for tag in toposort_flatten(tag_graph): tags.append(cpp.Tag( name=get_tag_name(tag), bases=[get_tag_name(b) for b in sorted(tag_graph[tag])], - index=index, id=tag, )) renderer.render(cpp.TagList(tags, opts.dbscheme), out / "TrapTags") diff --git a/swift/codegen/lib/cpp.py b/swift/codegen/lib/cpp.py index 9eb5f78af58..3542efcdee9 100644 --- a/swift/codegen/lib/cpp.py +++ b/swift/codegen/lib/cpp.py @@ -83,7 +83,6 @@ class TagBase: class Tag: name: str bases: List[TagBase] - index: int id: str def __post_init__(self): diff --git a/swift/codegen/schema.yml b/swift/codegen/schema.yml index f2e7837f2b2..bdf90320d5f 100644 --- a/swift/codegen/schema.yml +++ b/swift/codegen/schema.yml @@ -7,20 +7,23 @@ _includes: _directories: decl: Decl$|Context$ pattern: Pattern$ - type: Type$ - typerepr: TypeRepr$ + type: Type(Repr)?$ expr: Expr$ stmt: Stmt$ Element: - is_unknown: predicate + is_unknown: + type: predicate + _pragma: cpp_skip # this is emitted using trap entries directly _pragma: qltest_skip File: name: string Locatable: - location: Location? + location: + type: Location? + _pragma: cpp_skip # this is emitted using trap entries directly _pragma: qltest_skip Location: @@ -31,6 +34,10 @@ Location: end_column: int _pragma: qltest_skip +Comment: + _extends: Locatable + text: string + Type: name: string canonical_type: Type @@ -109,6 +116,7 @@ LValueType: ModuleType: _extends: Type + module: ModuleDecl PlaceholderType: _extends: Type @@ -181,6 +189,7 @@ Stmt: TypeRepr: _extends: AstNode + type: Type? # type can be absent on unresolved entities FunctionType: _extends: AnyFunctionType @@ -270,9 +279,22 @@ EnumCaseDecl: IfConfigDecl: _extends: Decl + _children: + clauses: IfConfigClause* + +IfConfigClause: + _extends: Locatable + _children: + condition: Expr? + elements: AstNode* + is_active: predicate + _dir: decl ImportDecl: _extends: Decl + is_exported: predicate + module: ModuleDecl + declarations: ValueDecl* MissingMemberDecl: _extends: Decl @@ -316,6 +338,7 @@ AppliedPropertyWrapperExpr: _extends: Expr Argument: + _extends: Locatable label: string _children: expr: Expr @@ -388,7 +411,6 @@ EnumIsCaseExpr: _extends: Expr _children: sub_expr: Expr - type_repr: TypeRepr element: EnumElementDecl ErrorExpr: @@ -439,7 +461,7 @@ KeyPathDotExpr: KeyPathExpr: _extends: Expr _children: - parsed_root: Expr? + root: TypeRepr? parsed_path: Expr? LazyInitializerExpr: @@ -542,7 +564,6 @@ TypeExpr: UnresolvedDeclRefExpr: _extends: Expr name: string? - _pragma: qltest_skip # we should really never extract these UnresolvedDotExpr: _extends: Expr @@ -557,7 +578,8 @@ UnresolvedMemberExpr: UnresolvedPatternExpr: _extends: Expr - _pragma: qltest_skip # we should really never extract these + _children: + sub_pattern: Pattern UnresolvedSpecializeExpr: _extends: Expr @@ -1067,6 +1089,8 @@ GenericTypeDecl: ModuleDecl: _extends: TypeDecl + is_builtin_module: predicate + is_system_module: predicate ConstructorRefCallExpr: _extends: SelfApplyExpr @@ -1157,87 +1181,3 @@ FloatLiteralExpr: IntegerLiteralExpr: _extends: NumberLiteralExpr string_value: string - -ErrorTypeRepr: - _extends: TypeRepr - -AttributedTypeRepr: - _extends: TypeRepr - -IdentTypeRepr: - _extends: TypeRepr - -ComponentIdentTypeRepr: - _extends: IdentTypeRepr - -SimpleIdentTypeRepr: - _extends: ComponentIdentTypeRepr - -GenericIdentTypeRepr: - _extends: ComponentIdentTypeRepr - -CompoundIdentTypeRepr: - _extends: IdentTypeRepr - -FunctionTypeRepr: - _extends: TypeRepr - -ArrayTypeRepr: - _extends: TypeRepr - -DictionaryTypeRepr: - _extends: TypeRepr - -OptionalTypeRepr: - _extends: TypeRepr - -ImplicitlyUnwrappedOptionalTypeRepr: - _extends: TypeRepr - -TupleTypeRepr: - _extends: TypeRepr - -CompositionTypeRepr: - _extends: TypeRepr - -MetatypeTypeRepr: - _extends: TypeRepr - -ProtocolTypeRepr: - _extends: TypeRepr - -OpaqueReturnTypeRepr: - _extends: TypeRepr - -NamedOpaqueReturnTypeRepr: - _extends: TypeRepr - -ExistentialTypeRepr: - _extends: TypeRepr - -PlaceholderTypeRepr: - _extends: TypeRepr - -SpecifierTypeRepr: - _extends: TypeRepr - -InOutTypeRepr: - _extends: SpecifierTypeRepr - -SharedTypeRepr: - _extends: SpecifierTypeRepr - -OwnedTypeRepr: - _extends: SpecifierTypeRepr - -IsolatedTypeRepr: - _extends: SpecifierTypeRepr - -CompileTimeConstTypeRepr: - _extends: SpecifierTypeRepr - -FixedTypeRepr: - _extends: TypeRepr - -SilBoxTypeRepr: - _extends: TypeRepr diff --git a/swift/codegen/templates/trap_tags_h.mustache b/swift/codegen/templates/trap_tags_h.mustache index feac64ff92a..c444d28afcb 100644 --- a/swift/codegen/templates/trap_tags_h.mustache +++ b/swift/codegen/templates/trap_tags_h.mustache @@ -7,7 +7,7 @@ namespace codeql { // {{id}} struct {{name}}Tag {{#has_bases}}: {{#bases}}{{^first}}, {{/first}}{{base}}Tag{{/bases}} {{/has_bases}}{ - static constexpr const char* prefix = "{{index}}"; + static constexpr const char* prefix = "{{name}}"; }; {{/tags}} } diff --git a/swift/codegen/test/test_cpp.py b/swift/codegen/test/test_cpp.py index e6cd4f81305..d04877e38c4 100644 --- a/swift/codegen/test/test_cpp.py +++ b/swift/codegen/test/test_cpp.py @@ -65,7 +65,7 @@ def test_trap_has_first_field_marked(): def test_tag_has_first_base_marked(): bases = ["a", "b", "c"] expected = [cpp.TagBase("a", first=True), cpp.TagBase("b"), cpp.TagBase("c")] - t = cpp.Tag("name", bases, 0, "id") + t = cpp.Tag("name", bases, "id") assert t.bases == expected @@ -75,7 +75,7 @@ def test_tag_has_first_base_marked(): (["a", "b"], True) ]) def test_tag_has_bases(bases, expected): - t = cpp.Tag("name", bases, 0, "id") + t = cpp.Tag("name", bases, "id") assert t.has_bases is expected diff --git a/swift/codegen/test/test_cppgen.py b/swift/codegen/test/test_cppgen.py index 160dd6cd2c1..7938cb6decd 100644 --- a/swift/codegen/test/test_cppgen.py +++ b/swift/codegen/test/test_cppgen.py @@ -165,5 +165,18 @@ def test_classes_with_dirs(generate_grouped): } +def test_cpp_skip_pragma(generate): + assert generate([ + schema.Class(name="A", properties=[ + schema.SingleProperty("x", "foo"), + schema.SingleProperty("y", "bar", pragmas=["x", "cpp_skip", "y"]), + ]) + ]) == [ + cpp.Class(name="A", final=True, trap_name="As", fields=[ + cpp.Field("x", "foo"), + ]), + ] + + if __name__ == '__main__': sys.exit(pytest.main([__file__] + sys.argv[1:])) diff --git a/swift/codegen/test/test_trapgen.py b/swift/codegen/test/test_trapgen.py index 376f69c43a3..c8121ce48ba 100644 --- a/swift/codegen/test/test_trapgen.py +++ b/swift/codegen/test/test_trapgen.py @@ -162,10 +162,10 @@ def test_one_union_tags(generate_tags): assert generate_tags([ dbscheme.Union(lhs="@left_hand_side", rhs=["@b", "@a", "@c"]), ]) == [ - cpp.Tag(name="LeftHandSide", bases=[], index=0, id="@left_hand_side"), - cpp.Tag(name="A", bases=["LeftHandSide"], index=1, id="@a"), - cpp.Tag(name="B", bases=["LeftHandSide"], index=2, id="@b"), - cpp.Tag(name="C", bases=["LeftHandSide"], index=3, id="@c"), + cpp.Tag(name="LeftHandSide", bases=[], id="@left_hand_side"), + cpp.Tag(name="A", bases=["LeftHandSide"], id="@a"), + cpp.Tag(name="B", bases=["LeftHandSide"], id="@b"), + cpp.Tag(name="C", bases=["LeftHandSide"], id="@c"), ] @@ -175,12 +175,12 @@ def test_multiple_union_tags(generate_tags): dbscheme.Union(lhs="@a", rhs=["@b", "@c"]), dbscheme.Union(lhs="@e", rhs=["@c", "@f"]), ]) == [ - cpp.Tag(name="D", bases=[], index=0, id="@d"), - cpp.Tag(name="E", bases=[], index=1, id="@e"), - cpp.Tag(name="A", bases=["D"], index=2, id="@a"), - cpp.Tag(name="F", bases=["E"], index=3, id="@f"), - cpp.Tag(name="B", bases=["A"], index=4, id="@b"), - cpp.Tag(name="C", bases=["A", "E"], index=5, id="@c"), + cpp.Tag(name="D", bases=[], id="@d"), + cpp.Tag(name="E", bases=[], id="@e"), + cpp.Tag(name="A", bases=["D"], id="@a"), + cpp.Tag(name="F", bases=["E"], id="@f"), + cpp.Tag(name="B", bases=["A"], id="@b"), + cpp.Tag(name="C", bases=["A", "E"], id="@c"), ] diff --git a/swift/extractor/BUILD.bazel b/swift/extractor/BUILD.bazel index b22841ca819..bf5c9e65d52 100644 --- a/swift/extractor/BUILD.bazel +++ b/swift/extractor/BUILD.bazel @@ -2,14 +2,10 @@ load("//swift:rules.bzl", "swift_cc_binary") swift_cc_binary( name = "extractor", - srcs = [ - "SwiftOutputRewrite.cpp", - "SwiftOutputRewrite.h", - "SwiftExtractor.cpp", - "SwiftExtractor.h", - "SwiftExtractorConfiguration.h", - "main.cpp", - ], + srcs = glob([ + "*.h", + "*.cpp", + ]), visibility = ["//swift:__pkg__"], deps = [ "//swift/extractor/infra", diff --git a/swift/extractor/SwiftExtractor.cpp b/swift/extractor/SwiftExtractor.cpp index 65f0ae150cc..f5a2fcb9e82 100644 --- a/swift/extractor/SwiftExtractor.cpp +++ b/swift/extractor/SwiftExtractor.cpp @@ -1,11 +1,7 @@ #include "SwiftExtractor.h" -#include -#include #include -#include #include -#include #include #include @@ -16,10 +12,12 @@ #include #include "swift/extractor/trap/generated/TrapClasses.h" -#include "swift/extractor/trap/TrapOutput.h" +#include "swift/extractor/trap/TrapDomain.h" #include "swift/extractor/visitors/SwiftVisitor.h" +#include "swift/extractor/TargetTrapFile.h" using namespace codeql; +using namespace std::string_literals; static void archiveFile(const SwiftExtractorConfiguration& config, swift::SourceFile& file) { if (std::error_code ec = llvm::sys::fs::create_directories(config.trapDir)) { @@ -52,102 +50,79 @@ static void archiveFile(const SwiftExtractorConfiguration& config, swift::Source } } -static std::string getTrapFilename(swift::ModuleDecl& module, swift::SourceFile* primaryFile) { +static std::string getFilename(swift::ModuleDecl& module, swift::SourceFile* primaryFile) { if (primaryFile) { return primaryFile->getFilename().str(); } - // Several modules with different name might come from .pcm (clang module) files - // In this case we want to differentiate them - std::string filename = module.getModuleFilename().str(); - filename += "-"; - filename += module.getName().str(); - return filename; + // PCM clang module + if (module.isNonSwiftModule()) { + // Several modules with different names might come from .pcm (clang module) files + // In this case we want to differentiate them + // Moreover, pcm files may come from caches located in different directories, but are + // unambiguously identified by the base file name, so we can discard the absolute directory + std::string filename = "/pcms/"s + llvm::sys::path::filename(module.getModuleFilename()).str(); + filename += "-"; + filename += module.getName().str(); + return filename; + } + return module.getModuleFilename().str(); +} + +static llvm::SmallVector getTopLevelDecls(swift::ModuleDecl& module, + swift::SourceFile* primaryFile = nullptr) { + llvm::SmallVector ret; + ret.push_back(&module); + if (primaryFile) { + primaryFile->getTopLevelDecls(ret); + } else { + module.getTopLevelDecls(ret); + } + return ret; } static void extractDeclarations(const SwiftExtractorConfiguration& config, - llvm::ArrayRef topLevelDecls, swift::CompilerInstance& compiler, swift::ModuleDecl& module, swift::SourceFile* primaryFile = nullptr) { + auto filename = getFilename(module, primaryFile); + // The extractor can be called several times from different processes with - // the same input file(s) - // We are using PID to avoid concurrent access - // TODO: find a more robust approach to avoid collisions? - auto name = getTrapFilename(module, primaryFile); - llvm::StringRef filename(name); - std::string tempTrapName = filename.str() + '.' + std::to_string(getpid()) + ".trap"; - llvm::SmallString tempTrapPath(config.getTempTrapDir()); - llvm::sys::path::append(tempTrapPath, tempTrapName); - - llvm::StringRef tempTrapParent = llvm::sys::path::parent_path(tempTrapPath); - if (std::error_code ec = llvm::sys::fs::create_directories(tempTrapParent)) { - std::cerr << "Cannot create temp trap directory '" << tempTrapParent.str() - << "': " << ec.message() << "\n"; + // the same input file(s). Using `TargetFile` the first process will win, and the following + // will just skip the work + auto trapTarget = createTargetTrapFile(config, filename); + if (!trapTarget) { + // another process arrived first, nothing to do for us return; } + TrapDomain trap{*trapTarget}; - std::ofstream trapStream(tempTrapPath.str().str()); - if (!trapStream) { - std::error_code ec; - ec.assign(errno, std::generic_category()); - std::cerr << "Cannot create temp trap file '" << tempTrapPath.str().str() - << "': " << ec.message() << "\n"; - return; - } - trapStream << "/* extractor-args:\n"; - for (auto opt : config.frontendOptions) { - trapStream << " " << std::quoted(opt) << " \\\n"; - } - trapStream << "\n*/\n"; - - trapStream << "/* swift-frontend-args:\n"; - for (auto opt : config.patchedFrontendOptions) { - trapStream << " " << std::quoted(opt) << " \\\n"; - } - trapStream << "\n*/\n"; - - TrapOutput trap{trapStream}; - TrapArena arena{}; - - // TODO: move default location emission elsewhere, possibly in a separate global trap file - auto unknownFileLabel = arena.allocateLabel(); + // TODO: remove this and recreate it with IPA when we have that // the following cannot conflict with actual files as those have an absolute path starting with / - trap.assignKey(unknownFileLabel, "unknown"); - trap.emit(FilesTrap{unknownFileLabel}); - auto unknownLocationLabel = arena.allocateLabel(); - trap.assignKey(unknownLocationLabel, "unknown"); - trap.emit(LocationsTrap{unknownLocationLabel, unknownFileLabel}); + File unknownFileEntry{trap.createLabel("unknown")}; + Location unknownLocationEntry{trap.createLabel("unknown")}; + unknownLocationEntry.file = unknownFileEntry.id; + trap.emit(unknownFileEntry); + trap.emit(unknownLocationEntry); - SwiftVisitor visitor(compiler.getSourceMgr(), arena, trap, module, primaryFile); + std::vector comments; + if (primaryFile && primaryFile->getBufferID().hasValue()) { + auto& sourceManager = compiler.getSourceMgr(); + auto tokens = swift::tokenize(compiler.getInvocation().getLangOptions(), sourceManager, + primaryFile->getBufferID().getValue()); + for (auto& token : tokens) { + if (token.getKind() == swift::tok::comment) { + comments.push_back(token); + } + } + } + + SwiftVisitor visitor(compiler.getSourceMgr(), trap, module, primaryFile); + auto topLevelDecls = getTopLevelDecls(module, primaryFile); for (auto decl : topLevelDecls) { visitor.extract(decl); } - if (topLevelDecls.empty()) { - // In the case of empty files, the dispatcher is not called, but we still want to 'record' the - // fact that the file was extracted - llvm::SmallString name(filename); - llvm::sys::fs::make_absolute(name); - auto fileLabel = arena.allocateLabel(); - trap.assignKey(fileLabel, name.str().str()); - trap.emit(FilesTrap{fileLabel, name.str().str()}); - } - - // TODO: Pick a better name to avoid collisions - std::string trapName = filename.str() + ".trap"; - llvm::SmallString trapPath(config.trapDir); - llvm::sys::path::append(trapPath, trapName); - - llvm::StringRef trapParent = llvm::sys::path::parent_path(trapPath); - if (std::error_code ec = llvm::sys::fs::create_directories(trapParent)) { - std::cerr << "Cannot create trap directory '" << trapParent.str() << "': " << ec.message() - << "\n"; - return; - } - - // TODO: The last process wins. Should we do better than that? - if (std::error_code ec = llvm::sys::fs::rename(tempTrapPath, trapPath)) { - std::cerr << "Cannot rename temp trap file '" << tempTrapPath.str().str() << "' -> '" - << trapPath.str().str() << "': " << ec.message() << "\n"; + for (auto& comment : comments) { + visitor.extract(comment); } } @@ -198,24 +173,21 @@ void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config, auto modules = collectModules(compiler); for (auto& module : modules) { - // We only extract system and builtin modules here as the other "user" modules can be built - // during the build process and then re-used at a later stage. In this case, we extract the - // user code twice: once during the module build in a form of a source file, and then as - // a pre-built module during building of the dependent source files. - if (module->isSystemModule() || module->isBuiltinModule()) { - llvm::SmallVector decls; - module->getTopLevelDecls(decls); - // TODO: pass ModuleDecl directly when we have module extraction in place? - extractDeclarations(config, decls, compiler, *module); - } else { - for (auto file : module->getFiles()) { - auto sourceFile = llvm::dyn_cast(file); - if (!sourceFile || inputFiles.count(sourceFile->getFilename().str()) == 0) { - continue; - } - archiveFile(config, *sourceFile); - extractDeclarations(config, sourceFile->getTopLevelDecls(), compiler, *module, sourceFile); + bool isFromSourceFile = false; + for (auto file : module->getFiles()) { + auto sourceFile = llvm::dyn_cast(file); + if (!sourceFile) { + continue; } + isFromSourceFile = true; + if (inputFiles.count(sourceFile->getFilename().str()) == 0) { + continue; + } + archiveFile(config, *sourceFile); + extractDeclarations(config, compiler, *module, sourceFile); + } + if (!isFromSourceFile) { + extractDeclarations(config, compiler, *module); } } } diff --git a/swift/extractor/SwiftExtractorConfiguration.h b/swift/extractor/SwiftExtractorConfiguration.h index 3365da5f268..cd4ed51cdcd 100644 --- a/swift/extractor/SwiftExtractorConfiguration.h +++ b/swift/extractor/SwiftExtractorConfiguration.h @@ -3,6 +3,8 @@ #include #include +#include "swift/extractor/infra/TargetFile.h" + namespace codeql { struct SwiftExtractorConfiguration { // The location for storing TRAP files to be imported by CodeQL engine. @@ -33,4 +35,5 @@ struct SwiftExtractorConfiguration { // overall extraction process. std::string getTempArtifactDir() const { return scratchDir + "/swift-extraction-artifacts"; } }; + } // namespace codeql diff --git a/swift/extractor/SwiftOutputRewrite.cpp b/swift/extractor/SwiftOutputRewrite.cpp index 35a38512ff8..b09a558d187 100644 --- a/swift/extractor/SwiftOutputRewrite.cpp +++ b/swift/extractor/SwiftOutputRewrite.cpp @@ -1,5 +1,6 @@ #include "SwiftOutputRewrite.h" #include "swift/extractor/SwiftExtractorConfiguration.h" +#include "swift/extractor/TargetTrapFile.h" #include #include @@ -163,7 +164,7 @@ static std::vector computeModuleAliases(llvm::StringRef modulePath, namespace codeql { std::unordered_map rewriteOutputsInPlace( - SwiftExtractorConfiguration& config, + const SwiftExtractorConfiguration& config, std::vector& CLIArgs) { std::unordered_map remapping; @@ -324,4 +325,17 @@ std::vector collectVFSFiles(const SwiftExtractorConfiguration& conf return overlays; } +void lockOutputSwiftModuleTraps(const SwiftExtractorConfiguration& config, + const std::unordered_map& remapping) { + for (const auto& [oldPath, newPath] : remapping) { + if (llvm::StringRef(oldPath).endswith(".swiftmodule")) { + if (auto target = createTargetTrapFile(config, oldPath)) { + *target << "// trap file deliberately empty\n" + "// this swiftmodule was created during the build, so its entities must have" + " been extracted directly from source files"; + } + } + } +} + } // namespace codeql diff --git a/swift/extractor/SwiftOutputRewrite.h b/swift/extractor/SwiftOutputRewrite.h index b7ee7fa3829..94f1eeb6aaa 100644 --- a/swift/extractor/SwiftOutputRewrite.h +++ b/swift/extractor/SwiftOutputRewrite.h @@ -13,7 +13,7 @@ struct SwiftExtractorConfiguration; // artifacts produced by the actual Swift compiler. // Returns the map containing remapping oldpath -> newPath. std::unordered_map rewriteOutputsInPlace( - SwiftExtractorConfiguration& config, + const SwiftExtractorConfiguration& config, std::vector& CLIArgs); // Create directories for all the redirected new paths as the Swift compiler expects them to exist. @@ -29,4 +29,7 @@ void storeRemappingForVFS(const SwiftExtractorConfiguration& config, // This is separate from storeRemappingForVFS as we also collect files produced by other processes. std::vector collectVFSFiles(const SwiftExtractorConfiguration& config); +// Creates empty trap files for output swiftmodule files +void lockOutputSwiftModuleTraps(const SwiftExtractorConfiguration& config, + const std::unordered_map& remapping); } // namespace codeql diff --git a/swift/extractor/TargetTrapFile.cpp b/swift/extractor/TargetTrapFile.cpp new file mode 100644 index 00000000000..2275575ecfa --- /dev/null +++ b/swift/extractor/TargetTrapFile.cpp @@ -0,0 +1,23 @@ +#include "swift/extractor/TargetTrapFile.h" +#include +namespace codeql { +std::optional createTargetTrapFile(const SwiftExtractorConfiguration& configuration, + std::string_view target) { + std::string trap{target}; + trap += ".trap"; + auto ret = TargetFile::create(trap, configuration.trapDir, configuration.getTempTrapDir()); + if (ret) { + *ret << "/* extractor-args:\n"; + for (const auto& opt : configuration.frontendOptions) { + *ret << " " << std::quoted(opt) << " \\\n"; + } + *ret << "\n*/\n" + "/* swift-frontend-args:\n"; + for (const auto& opt : configuration.patchedFrontendOptions) { + *ret << " " << std::quoted(opt) << " \\\n"; + } + *ret << "\n*/\n"; + } + return ret; +} +} // namespace codeql diff --git a/swift/extractor/TargetTrapFile.h b/swift/extractor/TargetTrapFile.h new file mode 100644 index 00000000000..eb8de4c206f --- /dev/null +++ b/swift/extractor/TargetTrapFile.h @@ -0,0 +1,11 @@ +#pragma once + +#include "swift/extractor/infra/TargetFile.h" +#include "swift/extractor/SwiftExtractorConfiguration.h" + +namespace codeql { + +std::optional createTargetTrapFile(const SwiftExtractorConfiguration& configuration, + std::string_view target); + +} // namespace codeql diff --git a/swift/extractor/infra/BUILD.bazel b/swift/extractor/infra/BUILD.bazel index 33098eb76a4..115fb06b745 100644 --- a/swift/extractor/infra/BUILD.bazel +++ b/swift/extractor/infra/BUILD.bazel @@ -2,9 +2,11 @@ load("//swift:rules.bzl", "swift_cc_library") swift_cc_library( name = "infra", + srcs = glob(["*.cpp"]), hdrs = glob(["*.h"]), visibility = ["//swift:__subpackages__"], deps = [ "//swift/extractor/trap", + "//swift/tools/prebuilt:swift-llvm-support", ], ) diff --git a/swift/extractor/infra/FilePath.h b/swift/extractor/infra/FilePath.h new file mode 100644 index 00000000000..48288b928e4 --- /dev/null +++ b/swift/extractor/infra/FilePath.h @@ -0,0 +1,24 @@ +#pragma once + +#include +namespace codeql { + +// wrapper around `std::string` mainly intended to unambiguously go into an `std::variant` +// TODO probably not needed once we can use `std::filesystem::path` +struct FilePath { + FilePath() = default; + FilePath(const std::string& path) : path{path} {} + FilePath(std::string&& path) : path{std::move(path)} {} + + std::string path; + + bool operator==(const FilePath& other) const { return path == other.path; } +}; +} // namespace codeql + +namespace std { +template <> +struct hash { + size_t operator()(const codeql::FilePath& value) { return hash{}(value.path); } +}; +} // namespace std diff --git a/swift/extractor/infra/SwiftDispatcher.h b/swift/extractor/infra/SwiftDispatcher.h index e7aaa5af612..6ef53211630 100644 --- a/swift/extractor/infra/SwiftDispatcher.h +++ b/swift/extractor/infra/SwiftDispatcher.h @@ -3,12 +3,13 @@ #include #include #include +#include -#include "swift/extractor/trap/TrapArena.h" #include "swift/extractor/trap/TrapLabelStore.h" -#include "swift/extractor/trap/TrapOutput.h" +#include "swift/extractor/trap/TrapDomain.h" #include "swift/extractor/infra/SwiftTagTraits.h" #include "swift/extractor/trap/generated/TrapClasses.h" +#include "swift/extractor/infra/FilePath.h" namespace codeql { @@ -18,19 +19,41 @@ namespace codeql { // Since SwiftDispatcher sees all the AST nodes, it also attaches a location to every 'locatable' // node (AST nodes that are not types: declarations, statements, expressions, etc.). class SwiftDispatcher { + // types to be supported by assignNewLabel/fetchLabel need to be listed here + using Store = TrapLabelStore; + + template + static constexpr bool IsStorable = std::is_constructible_v; + + template + static constexpr bool IsLocatable = std::is_base_of_v>; + public: // all references and pointers passed as parameters to this constructor are supposed to outlive // the SwiftDispatcher SwiftDispatcher(const swift::SourceManager& sourceManager, - TrapArena& arena, - TrapOutput& trap, + TrapDomain& trap, swift::ModuleDecl& currentModule, swift::SourceFile* currentPrimarySourceFile = nullptr) : sourceManager{sourceManager}, - arena{arena}, trap{trap}, currentModule{currentModule}, - currentPrimarySourceFile{currentPrimarySourceFile} {} + currentPrimarySourceFile{currentPrimarySourceFile} { + if (currentPrimarySourceFile) { + // we make sure the file is in the trap output even if the source is empty + fetchLabel(getFilePath(currentPrimarySourceFile->getFilename())); + } + } template void emit(const Entry& entry) { @@ -64,9 +87,11 @@ class SwiftDispatcher { // This method gives a TRAP label for already emitted AST node. // If the AST node was not emitted yet, then the emission is dispatched to a corresponding // visitor (see `visit(T *)` methods below). - template - TrapLabelOf fetchLabel(E* e) { - assert(e && "trying to fetch a label on nullptr, maybe fetchOptionalLabel is to be used?"); + template >* = nullptr> + TrapLabelOf fetchLabel(const E& e, Args&&... args) { + if constexpr (std::is_constructible_v) { + assert(e && "fetching a label on a null entity, maybe fetchOptionalLabel is to be used?"); + } // this is required so we avoid any recursive loop: a `fetchLabel` during the visit of `e` might // end up calling `fetchLabel` on `e` itself, so we want the visit of `e` to call `fetchLabel` // only after having called `assignNewLabel` on `e`. @@ -76,9 +101,10 @@ class SwiftDispatcher { return *l; } waitingForNewLabel = e; - visit(e); + visit(e, std::forward(args)...); + // TODO when everything is moved to structured C++ classes, this should be moved to createEntry if (auto l = store.get(e)) { - if constexpr (!std::is_base_of_v) { + if constexpr (IsLocatable) { attachLocation(e, *l); } return *l; @@ -95,35 +121,41 @@ class SwiftDispatcher { return fetchLabelFromUnion(node); } + template >* = nullptr> + TrapLabelOf fetchLabel(const E& e) { + return fetchLabel(&e); + } + // Due to the lazy emission approach, we must assign a label to a corresponding AST node before // it actually gets emitted to handle recursive cases such as recursive calls, or recursive type // declarations - template - TrapLabelOf assignNewLabel(E* e, Args&&... args) { + template >* = nullptr> + TrapLabelOf assignNewLabel(const E& e, Args&&... args) { assert(waitingForNewLabel == Store::Handle{e} && "assignNewLabel called on wrong entity"); - auto label = createLabel>(std::forward(args)...); + auto label = trap.createLabel>(std::forward(args)...); store.insert(e, label); waitingForNewLabel = std::monostate{}; return label; } - template >* = nullptr> + template >* = nullptr> TrapLabelOf assignNewLabel(const E& e, Args&&... args) { return assignNewLabel(&e, std::forward(args)...); } - template - TrapLabel createLabel() { - auto ret = arena.allocateLabel(); - trap.assignStar(ret); - return ret; + // convenience methods for structured C++ creation + template + auto createEntry(const E& e, Args&&... args) { + return TrapClassOf{assignNewLabel(e, std::forward(args)...)}; } - template - TrapLabel createLabel(Args&&... args) { - auto ret = arena.allocateLabel(); - trap.assignKey(ret, std::forward(args)...); - return ret; + // used to create a new entry for entities that should not be cached + // an example is swift::Argument, that are created on the fly and thus have no stable pointer + template + auto createUncachedEntry(const E& e, Args&&... args) { + auto label = trap.createLabel>(std::forward(args)...); + attachLocation(&e, label); + return TrapClassOf{label}; } template @@ -137,6 +169,15 @@ class SwiftDispatcher { attachLocation(locatable->getStartLoc(), locatable->getEndLoc(), locatableLabel); } + void attachLocation(const swift::IfConfigClause* clause, TrapLabel locatableLabel) { + attachLocation(clause->Loc, clause->Loc, locatableLabel); + } + + // Emits a Location TRAP entry and attaches it to a `Locatable` trap label for a given `SourceLoc` + void attachLocation(swift::SourceLoc loc, TrapLabel locatableLabel) { + attachLocation(loc, loc, locatableLabel); + } + // Emits a Location TRAP entry for a list of swift entities and attaches it to a `Locatable` trap // label template @@ -152,10 +193,10 @@ class SwiftDispatcher { // return `std::optional(fetchLabel(arg))` if arg converts to true, otherwise std::nullopt // universal reference `Arg&&` is used to catch both temporary and non-const references, not // for perfect forwarding - template - auto fetchOptionalLabel(Arg&& arg) -> std::optional { + template + auto fetchOptionalLabel(Arg&& arg, Args&&... args) -> std::optional { if (arg) { - return fetchLabel(arg); + return fetchLabel(arg, std::forward(args)...); } return std::nullopt; } @@ -173,6 +214,11 @@ class SwiftDispatcher { return ret; } + template + void emitDebugInfo(const Args&... args) { + trap.debug(args...); + } + // In order to not emit duplicated entries for declarations, we restrict emission to only // Decls declared within the current "scope". // Depending on the whether we are extracting a primary source file or not the scope is defined as @@ -186,7 +232,9 @@ class SwiftDispatcher { if (decl.getModuleContext() != ¤tModule) { return false; } - if (!currentPrimarySourceFile) { + // ModuleDecl is a special case: if it passed the previous test, it is the current module + // but it never has a source file, so we short circuit to emit it in any case + if (!currentPrimarySourceFile || decl.getKind() == swift::DeclKind::Module) { return true; } if (auto context = decl.getDeclContext()) { @@ -195,17 +243,13 @@ class SwiftDispatcher { return false; } - private: - // types to be supported by assignNewLabel/fetchLabel need to be listed here - using Store = TrapLabelStore; + void emitComment(swift::Token& comment) { + CommentsTrap entry{trap.createLabel(), comment.getRawText().str()}; + trap.emit(entry); + attachLocation(comment.getRange().getStart(), comment.getRange().getEnd(), entry.id); + } + private: void attachLocation(swift::SourceLoc start, swift::SourceLoc end, TrapLabel locatableLabel) { @@ -213,16 +257,16 @@ class SwiftDispatcher { // invalid locations seem to come from entities synthesized by the compiler return; } - std::string filepath = getFilepath(start); - auto fileLabel = createLabel(filepath); - // TODO: do not emit duplicate trap entries for Files - trap.emit(FilesTrap{fileLabel, filepath}); - auto [startLine, startColumn] = sourceManager.getLineAndColumnInBuffer(start); - auto [endLine, endColumn] = sourceManager.getLineAndColumnInBuffer(end); - auto locLabel = createLabel('{', fileLabel, "}:", startLine, ':', startColumn, ':', - endLine, ':', endColumn); - trap.emit(LocationsTrap{locLabel, fileLabel, startLine, startColumn, endLine, endColumn}); - trap.emit(LocatableLocationsTrap{locatableLabel, locLabel}); + auto file = getFilePath(sourceManager.getDisplayNameForLoc(start)); + Location entry{{}}; + entry.file = fetchLabel(file); + std::tie(entry.start_line, entry.start_column) = sourceManager.getLineAndColumnInBuffer(start); + std::tie(entry.end_line, entry.end_column) = sourceManager.getLineAndColumnInBuffer(end); + entry.id = trap.createLabel('{', entry.file, "}:", entry.start_line, ':', + entry.start_column, ':', entry.end_line, ':', + entry.end_column); + emit(entry); + emit(LocatableLocationsTrap{locatableLabel, entry.id}); } template @@ -238,40 +282,53 @@ class SwiftDispatcher { template bool fetchLabelFromUnionCase(const llvm::PointerUnion u, TrapLabel& output) { - if (auto e = u.template dyn_cast()) { - output = fetchLabel(e); - return true; + // we rely on the fact that when we extract `ASTNode` instances (which only happens + // on `BraceStmt` elements), we cannot encounter a standalone `TypeRepr` there, so we skip + // this case; extracting `TypeRepr`s here would be problematic as we would not be able to + // provide the corresponding type + if constexpr (!std::is_same_v) { + if (auto e = u.template dyn_cast()) { + output = fetchLabel(e); + return true; + } } return false; } - std::string getFilepath(swift::SourceLoc loc) { + static FilePath getFilePath(llvm::StringRef path) { // TODO: this needs more testing // TODO: check canonicaliztion of names on a case insensitive filesystems // TODO: make symlink resolution conditional on CODEQL_PRESERVE_SYMLINKS=true - auto displayName = sourceManager.getDisplayNameForLoc(loc); llvm::SmallString realPath; - if (std::error_code ec = llvm::sys::fs::real_path(displayName, realPath)) { - std::cerr << "Cannot get real path: '" << displayName.str() << "': " << ec.message() << "\n"; + if (std::error_code ec = llvm::sys::fs::real_path(path, realPath)) { + std::cerr << "Cannot get real path: '" << path.str() << "': " << ec.message() << "\n"; return {}; } return realPath.str().str(); } - // TODO: The following methods are supposed to redirect TRAP emission to correpsonding visitors, - // which are to be introduced in follow-up PRs + // TODO: for const correctness these should consistently be `const` (and maybe const references + // as we don't expect `nullptr` here. However `swift::ASTVisitor` and `swift::TypeVisitor` do not + // accept const pointers virtual void visit(swift::Decl* decl) = 0; + virtual void visit(const swift::IfConfigClause* clause) = 0; virtual void visit(swift::Stmt* stmt) = 0; - virtual void visit(swift::StmtCondition* cond) = 0; + virtual void visit(const swift::StmtCondition* cond) = 0; + virtual void visit(const swift::StmtConditionElement* cond) = 0; virtual void visit(swift::CaseLabelItem* item) = 0; virtual void visit(swift::Expr* expr) = 0; virtual void visit(swift::Pattern* pattern) = 0; - virtual void visit(swift::TypeRepr* type) = 0; + virtual void visit(swift::TypeRepr* typeRepr, swift::Type type) = 0; virtual void visit(swift::TypeBase* type) = 0; + void visit(const FilePath& file) { + auto entry = createEntry(file, file.path); + entry.name = file.path; + emit(entry); + } + const swift::SourceManager& sourceManager; - TrapArena& arena; - TrapOutput& trap; + TrapDomain& trap; Store store; Store::Handle waitingForNewLabel{std::monostate{}}; swift::ModuleDecl& currentModule; diff --git a/swift/extractor/infra/SwiftTagTraits.h b/swift/extractor/infra/SwiftTagTraits.h index 8a2e489683d..a03cf038111 100644 --- a/swift/extractor/infra/SwiftTagTraits.h +++ b/swift/extractor/infra/SwiftTagTraits.h @@ -5,6 +5,7 @@ #include #include "swift/extractor/trap/TrapTagTraits.h" #include "swift/extractor/trap/generated/TrapTags.h" +#include "swift/extractor/infra/FilePath.h" namespace codeql { @@ -14,14 +15,13 @@ using SILBlockStorageTypeTag = SilBlockStorageTypeTag; using SILBoxTypeTag = SilBoxTypeTag; using SILFunctionTypeTag = SilFunctionTypeTag; using SILTokenTypeTag = SilTokenTypeTag; -using SILBoxTypeReprTag = SilBoxTypeReprTag; -#define MAP_TYPE_TO_TAG(TYPE, TAG) \ - template <> \ - struct detail::ToTagFunctor { \ - using type = TAG; \ +#define MAP_TYPE_TO_TAG(TYPE, TAG) \ + template <> \ + struct detail::ToTagFunctor { \ + using type = TAG; \ } -#define MAP_TAG(TYPE) MAP_TYPE_TO_TAG(TYPE, TYPE##Tag) +#define MAP_TAG(TYPE) MAP_TYPE_TO_TAG(swift::TYPE, TYPE##Tag) #define MAP_SUBTAG(TYPE, PARENT) \ MAP_TAG(TYPE); \ static_assert(std::is_base_of_v, \ @@ -36,17 +36,20 @@ using SILBoxTypeReprTag = SilBoxTypeReprTag; MAP_TAG(Stmt); MAP_TAG(StmtCondition); +MAP_TYPE_TO_TAG(swift::StmtConditionElement, ConditionElementTag); MAP_TAG(CaseLabelItem); #define ABSTRACT_STMT(CLASS, PARENT) MAP_SUBTAG(CLASS##Stmt, PARENT) #define STMT(CLASS, PARENT) ABSTRACT_STMT(CLASS, PARENT) #include MAP_TAG(Expr); +MAP_TAG(Argument); #define ABSTRACT_EXPR(CLASS, PARENT) MAP_SUBTAG(CLASS##Expr, PARENT) #define EXPR(CLASS, PARENT) ABSTRACT_EXPR(CLASS, PARENT) #include MAP_TAG(Decl); +MAP_TAG(IfConfigClause); #define ABSTRACT_DECL(CLASS, PARENT) MAP_SUBTAG(CLASS##Decl, PARENT) #define DECL(CLASS, PARENT) ABSTRACT_DECL(CLASS, PARENT) #include @@ -57,11 +60,8 @@ MAP_TAG(Pattern); #include MAP_TAG(TypeRepr); -#define ABSTRACT_TYPEREPR(CLASS, PARENT) MAP_SUBTAG(CLASS##TypeRepr, PARENT) -#define TYPEREPR(CLASS, PARENT) ABSTRACT_TYPEREPR(CLASS, PARENT) -#include -MAP_TYPE_TO_TAG(TypeBase, TypeTag); +MAP_TYPE_TO_TAG(swift::TypeBase, TypeTag); #define ABSTRACT_TYPE(CLASS, PARENT) MAP_SUBTAG(CLASS##Type, PARENT) #define TYPE(CLASS, PARENT) ABSTRACT_TYPE(CLASS, PARENT) #include @@ -69,6 +69,8 @@ MAP_TYPE_TO_TAG(TypeBase, TypeTag); OVERRIDE_TAG(FuncDecl, ConcreteFuncDeclTag); OVERRIDE_TAG(VarDecl, ConcreteVarDeclTag); +MAP_TYPE_TO_TAG(FilePath, FileTag); + #undef MAP_TAG #undef MAP_SUBTAG #undef MAP_TYPE_TO_TAG diff --git a/swift/extractor/infra/TargetFile.cpp b/swift/extractor/infra/TargetFile.cpp new file mode 100644 index 00000000000..d9706205c90 --- /dev/null +++ b/swift/extractor/infra/TargetFile.cpp @@ -0,0 +1,85 @@ +#include "swift/extractor/infra/TargetFile.h" + +#include +#include +#include +#include + +#include +#include + +namespace codeql { +namespace { +[[noreturn]] void error(const char* action, const std::string& arg, std::error_code ec) { + std::cerr << "Unable to " << action << ": " << arg << " (" << ec.message() << ")\n"; + std::abort(); +} + +[[noreturn]] void error(const char* action, const std::string& arg) { + error(action, arg, {errno, std::system_category()}); +} + +void ensureParentDir(const std::string& path) { + auto parent = llvm::sys::path::parent_path(path); + if (auto ec = llvm::sys::fs::create_directories(parent)) { + error("create directory", parent.str(), ec); + } +} + +std::string initPath(std::string_view target, std::string_view dir) { + std::string ret{dir}; + assert(!target.empty() && "target must be a non-empty path"); + if (target[0] != '/') { + ret += '/'; + } + ret.append(target); + ensureParentDir(ret); + return ret; +} +} // namespace + +TargetFile::TargetFile(std::string_view target, + std::string_view targetDir, + std::string_view workingDir) + : workingPath{initPath(target, workingDir)}, targetPath{initPath(target, targetDir)} {} + +bool TargetFile::init() { + errno = 0; + // since C++17 "x" mode opens with O_EXCL (fails if file already exists) + if (auto f = std::fopen(targetPath.c_str(), "wx")) { + std::fclose(f); + out.open(workingPath); + checkOutput("open file for writing"); + return true; + } + if (errno != EEXIST) { + error("open file for writing", targetPath); + } + // else we just lost the race + return false; +} + +std::optional TargetFile::create(std::string_view target, + std::string_view targetDir, + std::string_view workingDir) { + TargetFile ret{target, targetDir, workingDir}; + if (ret.init()) return ret; + return std::nullopt; +} + +void TargetFile::commit() { + if (out.is_open()) { + out.close(); + errno = 0; + if (std::rename(workingPath.c_str(), targetPath.c_str()) != 0) { + error("rename file", targetPath); + } + } +} + +void TargetFile::checkOutput(const char* action) { + if (!out) { + error(action, workingPath); + } +} +} // namespace codeql diff --git a/swift/extractor/infra/TargetFile.h b/swift/extractor/infra/TargetFile.h new file mode 100644 index 00000000000..5f5ca7d823c --- /dev/null +++ b/swift/extractor/infra/TargetFile.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include + +namespace codeql { + +// Only the first process trying to create a `TargetFile` for a given `target` is allowed to do +// so, all others will have `create` return `std::nullopt`. +// The content streamed to the `TargetFile` is written to `workingDir/target`, and is moved onto +// `targetDir/target` on destruction. +class TargetFile { + std::string workingPath; + std::string targetPath; + std::ofstream out; + + public: + static std::optional create(std::string_view target, + std::string_view targetDir, + std::string_view workingDir); + + ~TargetFile() { commit(); } + + TargetFile(TargetFile&& other) = default; + // move assignment deleted as non-trivial and not needed + TargetFile& operator=(TargetFile&& other) = delete; + + template + TargetFile& operator<<(T&& value) { + errno = 0; + out << value; + checkOutput("write to file"); + return *this; + } + + private: + TargetFile(std::string_view target, std::string_view targetDir, std::string_view workingDir); + + bool init(); + void checkOutput(const char* action); + void commit(); +}; + +} // namespace codeql diff --git a/swift/extractor/main.cpp b/swift/extractor/main.cpp index bde37b0ccb5..5f9afef4e81 100644 --- a/swift/extractor/main.cpp +++ b/swift/extractor/main.cpp @@ -68,6 +68,7 @@ int main(int argc, char** argv) { codeql::rewriteOutputsInPlace(configuration, configuration.patchedFrontendOptions); codeql::ensureDirectoriesForNewPathsExist(remapping); codeql::storeRemappingForVFS(configuration, remapping); + codeql::lockOutputSwiftModuleTraps(configuration, remapping); std::vector args; for (auto& arg : configuration.patchedFrontendOptions) { diff --git a/swift/extractor/trap/BUILD.bazel b/swift/extractor/trap/BUILD.bazel index b1860142866..08875c45263 100644 --- a/swift/extractor/trap/BUILD.bazel +++ b/swift/extractor/trap/BUILD.bazel @@ -1,6 +1,6 @@ load("//swift:rules.bzl", "swift_cc_library") -_dirs = ("", "decl/", "expr/", "pattern/", "stmt/", "type/", "typerepr/") +_dirs = ("", "decl/", "expr/", "pattern/", "stmt/", "type/") genrule( name = "cppgen", diff --git a/swift/extractor/trap/TrapArena.h b/swift/extractor/trap/TrapArena.h deleted file mode 100644 index 99d0b4a5d3b..00000000000 --- a/swift/extractor/trap/TrapArena.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "swift/extractor/trap/TrapLabel.h" - -namespace codeql { - -// TrapArena has the responsibilities to allocate distinct trap #-labels -// TODO this is now a small functionality that will be moved to code upcoming from other PRs -class TrapArena { - uint64_t id_{0}; - - public: - template - TrapLabel allocateLabel() { - return TrapLabel::unsafeCreateFromExplicitId(id_++); - } -}; - -} // namespace codeql diff --git a/swift/extractor/trap/TrapOutput.h b/swift/extractor/trap/TrapDomain.h similarity index 56% rename from swift/extractor/trap/TrapOutput.h rename to swift/extractor/trap/TrapDomain.h index b9104b9118a..c84a3910134 100644 --- a/swift/extractor/trap/TrapOutput.h +++ b/swift/extractor/trap/TrapDomain.h @@ -1,19 +1,58 @@ #pragma once #include +#include + #include "swift/extractor/trap/TrapLabel.h" +#include "swift/extractor/infra/TargetFile.h" namespace codeql { -// Sink for trap emissions and label assignments. This abstracts away `ofstream` operations -// like `ofstream`, an explicit bool operator is provided, that return false if something -// went wrong -// TODO better error handling -class TrapOutput { - std::ostream& out_; +// Abstracts a given trap output file, with its own universe of trap labels +class TrapDomain { + TargetFile& out_; public: - explicit TrapOutput(std::ostream& out) : out_{out} {} + explicit TrapDomain(TargetFile& out) : out_{out} {} + + template + void emit(const Entry& e) { + print(e); + } + + template + void debug(const Args&... args) { + print("/* DEBUG:"); + print(args...); + print("*/"); + } + + template + TrapLabel createLabel() { + auto ret = allocateLabel(); + assignStar(ret); + return ret; + } + + template + TrapLabel createLabel(Args&&... args) { + auto ret = allocateLabel(); + assignKey(ret, std::forward(args)...); + return ret; + } + + private: + uint64_t id_{0}; + + template + TrapLabel allocateLabel() { + return TrapLabel::unsafeCreateFromExplicitId(id_++); + } + + template + void print(const Args&... args) { + (out_ << ... << args) << '\n'; + } template void assignStar(TrapLabel label) { @@ -33,23 +72,6 @@ class TrapOutput { (oss << ... << keyParts); assignKey(label, oss.str()); } - - template - void emit(const Entry& e) { - print(e); - } - - template - void debug(const Args&... args) { - out_ << "// DEBUG: "; - (out_ << ... << args) << '\n'; - } - - private: - template - void print(const Args&... args) { - (out_ << ... << args) << '\n'; - } }; } // namespace codeql diff --git a/swift/extractor/trap/TrapLabelStore.h b/swift/extractor/trap/TrapLabelStore.h index 93ca4212185..f9b0edd1753 100644 --- a/swift/extractor/trap/TrapLabelStore.h +++ b/swift/extractor/trap/TrapLabelStore.h @@ -20,10 +20,10 @@ namespace codeql { template class TrapLabelStore { public: - using Handle = std::variant; + using Handle = std::variant; template - std::optional> get(const T* e) { + std::optional> get(const T& e) { if (auto found = store_.find(e); found != store_.end()) { return TrapLabelOf::unsafeCreateFromUntyped(found->second); } @@ -31,7 +31,7 @@ class TrapLabelStore { } template - void insert(const T* e, TrapLabelOf l) { + void insert(const T& e, TrapLabelOf l) { auto [_, inserted] = store_.emplace(e, l); assert(inserted && "already inserted"); } diff --git a/swift/extractor/trap/TrapTagTraits.h b/swift/extractor/trap/TrapTagTraits.h index 9c29ff22330..f0f8c41cc8d 100644 --- a/swift/extractor/trap/TrapTagTraits.h +++ b/swift/extractor/trap/TrapTagTraits.h @@ -26,7 +26,8 @@ struct ToTrapClassFunctor; } // namespace detail template -using TrapTagOf = typename detail::ToTagOverride>::type; +using TrapTagOf = + typename detail::ToTagOverride>>::type; template using TrapLabelOf = TrapLabel>; diff --git a/swift/extractor/visitors/DeclVisitor.cpp b/swift/extractor/visitors/DeclVisitor.cpp index ed47f1e5fb1..fce28e504dd 100644 --- a/swift/extractor/visitors/DeclVisitor.cpp +++ b/swift/extractor/visitors/DeclVisitor.cpp @@ -83,11 +83,13 @@ codeql::PrecedenceGroupDecl DeclVisitor::translatePrecedenceGroupDecl( return entry; } -codeql::ParamDecl DeclVisitor::translateParamDecl(const swift::ParamDecl& decl) { - // TODO: deduplicate - ParamDecl entry{dispatcher_.assignNewLabel(decl)}; - fillVarDecl(decl, entry); - entry.is_inout = decl.isInOut(); +std::optional DeclVisitor::translateParamDecl(const swift::ParamDecl& decl) { + auto entry = createNamedEntry(decl); + if (!entry) { + return std::nullopt; + } + fillVarDecl(decl, *entry); + entry->is_inout = decl.isInOut(); return entry; } @@ -111,11 +113,20 @@ codeql::PatternBindingDecl DeclVisitor::translatePatternBindingDecl( return entry; } -codeql::ConcreteVarDecl DeclVisitor::translateVarDecl(const swift::VarDecl& decl) { - // TODO: deduplicate all non-local variables - ConcreteVarDecl entry{dispatcher_.assignNewLabel(decl)}; - entry.introducer_int = static_cast(decl.getIntroducer()); - fillVarDecl(decl, entry); +std::optional DeclVisitor::translateVarDecl(const swift::VarDecl& decl) { + std::optional entry; + // We do not deduplicate variables from non-swift (PCM, clang modules) modules as the mangler + // crashes sometimes + if (decl.getDeclContext()->isLocalContext() || decl.getModuleContext()->isNonSwiftModule()) { + entry.emplace(dispatcher_.assignNewLabel(decl)); + } else { + entry = createNamedEntry(decl); + if (!entry) { + return std::nullopt; + } + } + entry->introducer_int = static_cast(decl.getIntroducer()); + fillVarDecl(decl, *entry); return entry; } @@ -241,16 +252,15 @@ std::variant DeclVisitor::trans std::optional DeclVisitor::translateSubscriptDecl( const swift::SubscriptDecl& decl) { - auto id = dispatcher_.assignNewLabel(decl, mangledName(decl)); - if (!dispatcher_.shouldEmitDeclBody(decl)) { + auto entry = createNamedEntry(decl); + if (!entry) { return std::nullopt; } - SubscriptDecl entry{id}; - entry.element_type = dispatcher_.fetchLabel(decl.getElementInterfaceType()); + entry->element_type = dispatcher_.fetchLabel(decl.getElementInterfaceType()); if (auto indices = decl.getIndices()) { - entry.params = dispatcher_.fetchRepeatedLabels(*indices); + entry->params = dispatcher_.fetchRepeatedLabels(*indices); } - fillAbstractStorageDecl(decl, entry); + fillAbstractStorageDecl(decl, *entry); return entry; } @@ -262,9 +272,53 @@ codeql::ExtensionDecl DeclVisitor::translateExtensionDecl(const swift::Extension return entry; } +codeql::ImportDecl DeclVisitor::translateImportDecl(const swift::ImportDecl& decl) { + auto entry = dispatcher_.createEntry(decl); + entry.is_exported = decl.isExported(); + entry.module = dispatcher_.fetchLabel(decl.getModule()); + entry.declarations = dispatcher_.fetchRepeatedLabels(decl.getDecls()); + return entry; +} + +std::optional DeclVisitor::translateModuleDecl(const swift::ModuleDecl& decl) { + auto entry = createNamedEntry(decl); + if (!entry) { + return std::nullopt; + } + entry->is_builtin_module = decl.isBuiltinModule(); + entry->is_system_module = decl.isSystemModule(); + fillTypeDecl(decl, *entry); + return entry; +} + std::string DeclVisitor::mangledName(const swift::ValueDecl& decl) { - // prefix adds a couple of special symbols, we don't necessary need them - return mangler.mangleAnyDecl(&decl, /* prefix = */ false); + // ASTMangler::mangleAnyDecl crashes when called on `ModuleDecl` + // TODO find a more unique string working also when different modules are compiled with the same + // name + std::ostringstream ret; + if (decl.getKind() == swift::DeclKind::Module) { + ret << static_cast(decl).getRealName().str().str(); + } else if (decl.getKind() == swift::DeclKind::TypeAlias) { + // In cases like this (when coming from PCM) + // typealias CFXMLTree = CFTree + // typealias CFXMLTreeRef = CFXMLTree + // mangleAnyDecl mangles both CFXMLTree and CFXMLTreeRef into 'So12CFXMLTreeRefa' + // which is not correct and causes inconsistencies. mangleEntity makes these two distinct + // prefix adds a couple of special symbols, we don't necessary need them + ret << mangler.mangleEntity(&decl); + } else { + // prefix adds a couple of special symbols, we don't necessary need them + ret << mangler.mangleAnyDecl(&decl, /* prefix = */ false); + } + // there can be separate declarations (`VarDecl` or `AccessorDecl`) which are effectively the same + // (with equal mangled name) but come from different clang modules. This is the case for example + // for glibc constants like `L_SET` that appear in both `SwiftGlibc` and `CDispatch`. + // For the moment, we sidestep the problem by making them separate entities in the DB + // TODO find a more solid solution + if (decl.getModuleContext()->isNonSwiftModule()) { + ret << '_' << decl.getModuleContext()->getRealName().str().str(); + } + return ret.str(); } void DeclVisitor::fillAbstractFunctionDecl(const swift::AbstractFunctionDecl& decl, @@ -334,4 +388,18 @@ void DeclVisitor::fillAbstractStorageDecl(const swift::AbstractStorageDecl& decl fillValueDecl(decl, entry); } +codeql::IfConfigDecl DeclVisitor::translateIfConfigDecl(const swift::IfConfigDecl& decl) { + auto entry = dispatcher_.createEntry(decl); + entry.clauses = dispatcher_.fetchRepeatedLabels(decl.getClauses()); + return entry; +} + +codeql::IfConfigClause DeclVisitor::translateIfConfigClause(const swift::IfConfigClause& clause) { + auto entry = dispatcher_.createEntry(clause); + entry.condition = dispatcher_.fetchOptionalLabel(clause.Cond); + entry.elements = dispatcher_.fetchRepeatedLabels(clause.Elements); + entry.is_active = clause.isActive; + return entry; +} + } // namespace codeql diff --git a/swift/extractor/visitors/DeclVisitor.h b/swift/extractor/visitors/DeclVisitor.h index 0527513d859..85e819adb5b 100644 --- a/swift/extractor/visitors/DeclVisitor.h +++ b/swift/extractor/visitors/DeclVisitor.h @@ -14,6 +14,11 @@ namespace codeql { class DeclVisitor : public AstVisitorBase { public: using AstVisitorBase::AstVisitorBase; + using AstVisitorBase::visit; + + void visit(const swift::IfConfigClause* clause) { + dispatcher_.emit(translateIfConfigClause(*clause)); + } std::variant translateFuncDecl( const swift::FuncDecl& decl); @@ -25,10 +30,10 @@ class DeclVisitor : public AstVisitorBase { codeql::PostfixOperatorDecl translatePostfixOperatorDecl(const swift::PostfixOperatorDecl& decl); codeql::InfixOperatorDecl translateInfixOperatorDecl(const swift::InfixOperatorDecl& decl); codeql::PrecedenceGroupDecl translatePrecedenceGroupDecl(const swift::PrecedenceGroupDecl& decl); - codeql::ParamDecl translateParamDecl(const swift::ParamDecl& decl); + std::optional translateParamDecl(const swift::ParamDecl& decl); codeql::TopLevelCodeDecl translateTopLevelCodeDecl(const swift::TopLevelCodeDecl& decl); codeql::PatternBindingDecl translatePatternBindingDecl(const swift::PatternBindingDecl& decl); - codeql::ConcreteVarDecl translateVarDecl(const swift::VarDecl& decl); + std::optional translateVarDecl(const swift::VarDecl& decl); std::variant translateStructDecl( const swift::StructDecl& decl); std::variant translateClassDecl( @@ -50,6 +55,10 @@ class DeclVisitor : public AstVisitorBase { const swift::AccessorDecl& decl); std::optional translateSubscriptDecl(const swift::SubscriptDecl& decl); codeql::ExtensionDecl translateExtensionDecl(const swift::ExtensionDecl& decl); + codeql::ImportDecl translateImportDecl(const swift::ImportDecl& decl); + std::optional translateModuleDecl(const swift::ModuleDecl& decl); + codeql::IfConfigDecl translateIfConfigDecl(const swift::IfConfigDecl& decl); + codeql::IfConfigClause translateIfConfigClause(const swift::IfConfigClause& clause); private: std::string mangledName(const swift::ValueDecl& decl); @@ -66,6 +75,15 @@ class DeclVisitor : public AstVisitorBase { void fillAbstractStorageDecl(const swift::AbstractStorageDecl& decl, codeql::AbstractStorageDecl& entry); + template + std::optional> createNamedEntry(const D& decl) { + auto id = dispatcher_.assignNewLabel(decl, mangledName(decl)); + if (dispatcher_.shouldEmitDeclBody(decl)) { + return TrapClassOf{id}; + } + return std::nullopt; + } + private: swift::Mangle::ASTMangler mangler; }; diff --git a/swift/extractor/visitors/ExprVisitor.cpp b/swift/extractor/visitors/ExprVisitor.cpp index e6aabef4cea..a4bc1639811 100644 --- a/swift/extractor/visitors/ExprVisitor.cpp +++ b/swift/extractor/visitors/ExprVisitor.cpp @@ -188,9 +188,8 @@ void ExprVisitor::visitEnumIsCaseExpr(swift::EnumIsCaseExpr* expr) { assert(expr->getCaseTypeRepr() && "EnumIsCaseExpr has CaseTypeRepr"); assert(expr->getEnumElement() && "EnumIsCaseExpr has EnumElement"); auto subExpr = dispatcher_.fetchLabel(expr->getSubExpr()); - auto typeRepr = dispatcher_.fetchLabel(expr->getCaseTypeRepr()); auto enumElement = dispatcher_.fetchLabel(expr->getEnumElement()); - dispatcher_.emit(EnumIsCaseExprsTrap{label, subExpr, typeRepr, enumElement}); + dispatcher_.emit(EnumIsCaseExprsTrap{label, subExpr, enumElement}); } void ExprVisitor::visitMakeTemporarilyEscapableExpr(swift::MakeTemporarilyEscapableExpr* expr) { @@ -288,7 +287,9 @@ void ExprVisitor::visitErasureExpr(swift::ErasureExpr* expr) { codeql::TypeExpr ExprVisitor::translateTypeExpr(const swift::TypeExpr& expr) { TypeExpr entry{dispatcher_.assignNewLabel(expr)}; - entry.type_repr = dispatcher_.fetchOptionalLabel(expr.getTypeRepr()); + if (expr.getTypeRepr() && expr.getInstanceType()) { + entry.type_repr = dispatcher_.fetchLabel(expr.getTypeRepr(), expr.getInstanceType()); + } return entry; } @@ -478,9 +479,14 @@ void ExprVisitor::visitKeyPathExpr(swift::KeyPathExpr* expr) { auto pathLabel = dispatcher_.fetchLabel(path); dispatcher_.emit(KeyPathExprParsedPathsTrap{label, pathLabel}); } - if (auto root = expr->getParsedRoot()) { - auto rootLabel = dispatcher_.fetchLabel(root); - dispatcher_.emit(KeyPathExprParsedRootsTrap{label, rootLabel}); + // TODO maybe move this logic to QL? + if (auto rootTypeRepr = expr->getRootType()) { + auto keyPathType = expr->getType()->getAs(); + assert(keyPathType && "KeyPathExpr must have BoundGenericClassType"); + auto keyPathTypeArgs = keyPathType->getGenericArgs(); + assert(keyPathTypeArgs.size() != 0 && "KeyPathExpr type must have generic args"); + auto rootLabel = dispatcher_.fetchLabel(rootTypeRepr, keyPathTypeArgs[0]); + dispatcher_.emit(KeyPathExprRootsTrap{label, rootLabel}); } } } @@ -611,11 +617,11 @@ void ExprVisitor::fillAbstractClosureExpr(const swift::AbstractClosureExpr& expr } TrapLabel ExprVisitor::emitArgument(const swift::Argument& arg) { - auto argLabel = dispatcher_.createLabel(); - assert(arg.getExpr() && "Argument has getExpr"); - dispatcher_.emit( - ArgumentsTrap{argLabel, arg.getLabel().str().str(), dispatcher_.fetchLabel(arg.getExpr())}); - return argLabel; + auto entry = dispatcher_.createUncachedEntry(arg); + entry.label = arg.getLabel().str().str(); + entry.expr = dispatcher_.fetchLabel(arg.getExpr()); + dispatcher_.emit(entry); + return entry.id; } void ExprVisitor::emitImplicitConversionExpr(swift::ImplicitConversionExpr* expr, @@ -668,4 +674,10 @@ void ExprVisitor::emitLookupExpr(const swift::LookupExpr* expr, TrapLabel { codeql::BridgeFromObjCExpr translateBridgeFromObjCExpr(const swift::BridgeFromObjCExpr& expr); codeql::DotSelfExpr translateDotSelfExpr(const swift::DotSelfExpr& expr); codeql::ErrorExpr translateErrorExpr(const swift::ErrorExpr& expr); + // The following function requires a non-const parameter because: + // * `swift::UnresolvedPatternExpr::getSubPattern` has a `const`-qualified overload returning + // `const swift::Pattern*` + // * `swift::ASTVisitor` only visits non-const pointers + // either we accept this, or we fix constness, e.g. by providing `visit` on `const` pointers + // in `VisitorBase`, or by doing a `const_cast` in `SwifDispatcher::fetchLabel` + codeql::UnresolvedPatternExpr translateUnresolvedPatternExpr(swift::UnresolvedPatternExpr& expr); private: void fillAbstractClosureExpr(const swift::AbstractClosureExpr& expr, diff --git a/swift/extractor/visitors/PatternVisitor.cpp b/swift/extractor/visitors/PatternVisitor.cpp index 43421fcbede..4ef90aa56a4 100644 --- a/swift/extractor/visitors/PatternVisitor.cpp +++ b/swift/extractor/visitors/PatternVisitor.cpp @@ -18,8 +18,8 @@ void PatternVisitor::visitTypedPattern(swift::TypedPattern* pattern) { assert(pattern->getSubPattern() && "expect TypedPattern to have a SubPattern"); dispatcher_.emit(TypedPatternsTrap{label, dispatcher_.fetchLabel(pattern->getSubPattern())}); if (auto typeRepr = pattern->getTypeRepr()) { - dispatcher_.emit( - TypedPatternTypeReprsTrap{label, dispatcher_.fetchLabel(pattern->getTypeRepr())}); + dispatcher_.emit(TypedPatternTypeReprsTrap{ + label, dispatcher_.fetchLabel(pattern->getTypeRepr(), pattern->getType())}); } } @@ -63,7 +63,8 @@ void PatternVisitor::visitIsPattern(swift::IsPattern* pattern) { dispatcher_.emit(IsPatternsTrap{label}); if (auto typeRepr = pattern->getCastTypeRepr()) { - dispatcher_.emit(IsPatternCastTypeReprsTrap{label, dispatcher_.fetchLabel(typeRepr)}); + dispatcher_.emit(IsPatternCastTypeReprsTrap{ + label, dispatcher_.fetchLabel(typeRepr, pattern->getCastType())}); } if (auto subPattern = pattern->getSubPattern()) { dispatcher_.emit(IsPatternSubPatternsTrap{label, dispatcher_.fetchLabel(subPattern)}); diff --git a/swift/extractor/visitors/StmtVisitor.cpp b/swift/extractor/visitors/StmtVisitor.cpp index 21f93828ed7..bc8a8a30933 100644 --- a/swift/extractor/visitors/StmtVisitor.cpp +++ b/swift/extractor/visitors/StmtVisitor.cpp @@ -7,26 +7,22 @@ void StmtVisitor::visitLabeledStmt(swift::LabeledStmt* stmt) { emitLabeledStmt(stmt, label); } -void StmtVisitor::visitStmtCondition(swift::StmtCondition* cond) { - auto label = dispatcher_.assignNewLabel(cond); - dispatcher_.emit(StmtConditionsTrap{label}); - unsigned index = 0; - for (const auto& cond : *cond) { - auto condLabel = dispatcher_.createLabel(); - dispatcher_.attachLocation(cond, condLabel); - dispatcher_.emit(ConditionElementsTrap{condLabel}); - dispatcher_.emit(StmtConditionElementsTrap{label, index++, condLabel}); - if (auto boolean = cond.getBooleanOrNull()) { - auto elementLabel = dispatcher_.fetchLabel(boolean); - dispatcher_.emit(ConditionElementBooleansTrap{condLabel, elementLabel}); - } else if (auto pattern = cond.getPatternOrNull()) { - auto patternLabel = dispatcher_.fetchLabel(pattern); - auto initilizerLabel = dispatcher_.fetchLabel(cond.getInitializer()); - dispatcher_.emit(ConditionElementPatternsTrap{condLabel, patternLabel}); - dispatcher_.emit(ConditionElementInitializersTrap{condLabel, initilizerLabel}); - } - /// TODO: Implement availability +codeql::StmtCondition StmtVisitor::translateStmtCondition(const swift::StmtCondition& cond) { + auto entry = dispatcher_.createEntry(cond); + entry.elements = dispatcher_.fetchRepeatedLabels(cond); + return entry; +} + +codeql::ConditionElement StmtVisitor::translateStmtConditionElement( + const swift::StmtConditionElement& element) { + auto entry = dispatcher_.createEntry(element); + if (auto boolean = element.getBooleanOrNull()) { + entry.boolean = dispatcher_.fetchLabel(boolean); + } else if (auto pattern = element.getPatternOrNull()) { + entry.pattern = dispatcher_.fetchLabel(pattern); + entry.initializer = dispatcher_.fetchLabel(element.getInitializer()); } + return entry; } void StmtVisitor::visitLabeledConditionalStmt(swift::LabeledConditionalStmt* stmt) { diff --git a/swift/extractor/visitors/StmtVisitor.h b/swift/extractor/visitors/StmtVisitor.h index 78273366d9e..e929a364399 100644 --- a/swift/extractor/visitors/StmtVisitor.h +++ b/swift/extractor/visitors/StmtVisitor.h @@ -10,7 +10,9 @@ class StmtVisitor : public AstVisitorBase { using AstVisitorBase::AstVisitorBase; void visitLabeledStmt(swift::LabeledStmt* stmt); - void visitStmtCondition(swift::StmtCondition* cond); + codeql::StmtCondition translateStmtCondition(const swift::StmtCondition& cond); + codeql::ConditionElement translateStmtConditionElement( + const swift::StmtConditionElement& element); void visitLabeledConditionalStmt(swift::LabeledConditionalStmt* stmt); void visitCaseLabelItem(swift::CaseLabelItem* labelItem); void visitBraceStmt(swift::BraceStmt* stmt); diff --git a/swift/extractor/visitors/SwiftVisitor.h b/swift/extractor/visitors/SwiftVisitor.h index 6380390af82..f1828c39689 100644 --- a/swift/extractor/visitors/SwiftVisitor.h +++ b/swift/extractor/visitors/SwiftVisitor.h @@ -5,7 +5,6 @@ #include "swift/extractor/visitors/ExprVisitor.h" #include "swift/extractor/visitors/StmtVisitor.h" #include "swift/extractor/visitors/TypeVisitor.h" -#include "swift/extractor/visitors/TypeReprVisitor.h" #include "swift/extractor/visitors/PatternVisitor.h" namespace codeql { @@ -15,24 +14,32 @@ class SwiftVisitor : private SwiftDispatcher { using SwiftDispatcher::SwiftDispatcher; template - void extract(T* entity) { + void extract(const T& entity) { fetchLabel(entity); } + void extract(swift::Token& comment) { emitComment(comment); } private: void visit(swift::Decl* decl) override { declVisitor.visit(decl); } + void visit(const swift::IfConfigClause* clause) override { declVisitor.visit(clause); } void visit(swift::Stmt* stmt) override { stmtVisitor.visit(stmt); } - void visit(swift::StmtCondition* cond) override { stmtVisitor.visitStmtCondition(cond); } + void visit(const swift::StmtCondition* cond) override { + emit(stmtVisitor.translateStmtCondition(*cond)); + } + void visit(const swift::StmtConditionElement* element) override { + emit(stmtVisitor.translateStmtConditionElement(*element)); + } void visit(swift::CaseLabelItem* item) override { stmtVisitor.visitCaseLabelItem(item); } void visit(swift::Expr* expr) override { exprVisitor.visit(expr); } void visit(swift::Pattern* pattern) override { patternVisitor.visit(pattern); } - void visit(swift::TypeRepr* type) override { typeReprVisitor.visit(type); } void visit(swift::TypeBase* type) override { typeVisitor.visit(type); } + void visit(swift::TypeRepr* typeRepr, swift::Type type) override { + emit(typeVisitor.translateTypeRepr(*typeRepr, type)); + } DeclVisitor declVisitor{*this}; ExprVisitor exprVisitor{*this}; StmtVisitor stmtVisitor{*this}; - TypeReprVisitor typeReprVisitor{*this}; TypeVisitor typeVisitor{*this}; PatternVisitor patternVisitor{*this}; }; diff --git a/swift/extractor/visitors/TypeReprVisitor.cpp b/swift/extractor/visitors/TypeReprVisitor.cpp deleted file mode 100644 index a3daa7938bb..00000000000 --- a/swift/extractor/visitors/TypeReprVisitor.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "swift/extractor/visitors/TypeReprVisitor.h" - -namespace codeql {} // namespace codeql diff --git a/swift/extractor/visitors/TypeReprVisitor.h b/swift/extractor/visitors/TypeReprVisitor.h deleted file mode 100644 index dba2198cc6c..00000000000 --- a/swift/extractor/visitors/TypeReprVisitor.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "swift/extractor/visitors/VisitorBase.h" -#include "swift/extractor/trap/generated/typerepr/TrapClasses.h" - -namespace codeql { - -class TypeReprVisitor : public AstVisitorBase { - public: - using AstVisitorBase::AstVisitorBase; -}; - -} // namespace codeql diff --git a/swift/extractor/visitors/TypeVisitor.cpp b/swift/extractor/visitors/TypeVisitor.cpp index 4514692421e..d1c499a9a96 100644 --- a/swift/extractor/visitors/TypeVisitor.cpp +++ b/swift/extractor/visitors/TypeVisitor.cpp @@ -1,4 +1,5 @@ #include "swift/extractor/visitors/TypeVisitor.h" + namespace codeql { void TypeVisitor::visit(swift::TypeBase* type) { TypeVisitorBase::visit(type); @@ -8,6 +9,12 @@ void TypeVisitor::visit(swift::TypeBase* type) { dispatcher_.emit(TypesTrap{label, type->getString(), canonicalLabel}); } +codeql::TypeRepr TypeVisitor::translateTypeRepr(const swift::TypeRepr& typeRepr, swift::Type type) { + auto entry = dispatcher_.createEntry(typeRepr); + entry.type = dispatcher_.fetchOptionalLabel(type); + return entry; +} + void TypeVisitor::visitProtocolType(swift::ProtocolType* type) { auto label = dispatcher_.assignNewLabel(type); dispatcher_.emit(ProtocolTypesTrap{label}); @@ -122,13 +129,13 @@ void TypeVisitor::visitParenType(swift::ParenType* type) { } codeql::OptionalType TypeVisitor::translateOptionalType(const swift::OptionalType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); fillUnarySyntaxSugarType(type, entry); return entry; } codeql::ArraySliceType TypeVisitor::translateArraySliceType(const swift::ArraySliceType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); fillUnarySyntaxSugarType(type, entry); return entry; } @@ -163,7 +170,7 @@ void TypeVisitor::visitLValueType(swift::LValueType* type) { codeql::PrimaryArchetypeType TypeVisitor::translatePrimaryArchetypeType( const swift::PrimaryArchetypeType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); fillArchetypeType(type, entry); return entry; } @@ -229,7 +236,7 @@ void TypeVisitor::emitAnyGenericType(swift::AnyGenericType* type, codeql::NestedArchetypeType TypeVisitor::translateNestedArchetypeType( const swift::NestedArchetypeType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); entry.parent = dispatcher_.fetchLabel(type.getParent()); entry.associated_type_declaration = dispatcher_.fetchLabel(type.getAssocType()); fillArchetypeType(type, entry); @@ -248,26 +255,26 @@ void TypeVisitor::fillArchetypeType(const swift::ArchetypeType& type, ArchetypeT } codeql::ExistentialType TypeVisitor::translateExistentialType(const swift::ExistentialType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); entry.constraint = dispatcher_.fetchLabel(type.getConstraintType()); return entry; } codeql::DynamicSelfType TypeVisitor::translateDynamicSelfType(const swift::DynamicSelfType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); entry.static_self_type = dispatcher_.fetchLabel(type.getSelfType()); return entry; } codeql::VariadicSequenceType TypeVisitor::translateVariadicSequenceType( const swift::VariadicSequenceType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); fillUnarySyntaxSugarType(type, entry); return entry; } codeql::InOutType TypeVisitor::translateInOutType(const swift::InOutType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); entry.object_type = dispatcher_.fetchLabel(type.getObjectType()); return entry; } @@ -300,19 +307,19 @@ void TypeVisitor::fillReferenceStorageType(const swift::ReferenceStorageType& ty codeql::ProtocolCompositionType TypeVisitor::translateProtocolCompositionType( const swift::ProtocolCompositionType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); entry.members = dispatcher_.fetchRepeatedLabels(type.getMembers()); return entry; } codeql::BuiltinIntegerLiteralType TypeVisitor::translateBuiltinIntegerLiteralType( const swift::BuiltinIntegerLiteralType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinIntegerType TypeVisitor::translateBuiltinIntegerType( const swift::BuiltinIntegerType& type) { - auto entry = createEntry(type); + auto entry = createTypeEntry(type); if (type.isFixedWidth()) { entry.width = type.getFixedWidth(); } @@ -321,51 +328,67 @@ codeql::BuiltinIntegerType TypeVisitor::translateBuiltinIntegerType( codeql::BuiltinBridgeObjectType TypeVisitor::translateBuiltinBridgeObjectType( const swift::BuiltinBridgeObjectType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinDefaultActorStorageType TypeVisitor::translateBuiltinDefaultActorStorageType( const swift::BuiltinDefaultActorStorageType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinExecutorType TypeVisitor::translateBuiltinExecutorType( const swift::BuiltinExecutorType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinFloatType TypeVisitor::translateBuiltinFloatType( const swift::BuiltinFloatType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinJobType TypeVisitor::translateBuiltinJobType(const swift::BuiltinJobType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinNativeObjectType TypeVisitor::translateBuiltinNativeObjectType( const swift::BuiltinNativeObjectType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinRawPointerType TypeVisitor::translateBuiltinRawPointerType( const swift::BuiltinRawPointerType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinRawUnsafeContinuationType TypeVisitor::translateBuiltinRawUnsafeContinuationType( const swift::BuiltinRawUnsafeContinuationType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinUnsafeValueBufferType TypeVisitor::translateBuiltinUnsafeValueBufferType( const swift::BuiltinUnsafeValueBufferType& type) { - return createEntry(type); + return createTypeEntry(type); } codeql::BuiltinVectorType TypeVisitor::translateBuiltinVectorType( const swift::BuiltinVectorType& type) { - return createEntry(type); + return createTypeEntry(type); } +codeql::OpenedArchetypeType TypeVisitor::translateOpenedArchetypeType( + const swift::OpenedArchetypeType& type) { + auto entry = createTypeEntry(type); + fillArchetypeType(type, entry); + return entry; +} + +codeql::ModuleType TypeVisitor::translateModuleType(const swift::ModuleType& type) { + auto key = type.getModule()->getRealName().str().str(); + if (type.getModule()->isNonSwiftModule()) { + key += "|clang"; + } + auto entry = createTypeEntry(type, key); + entry.module = dispatcher_.fetchLabel(type.getModule()); + return entry; +} } // namespace codeql diff --git a/swift/extractor/visitors/TypeVisitor.h b/swift/extractor/visitors/TypeVisitor.h index 99d1ca7fdbb..21cbd96d24d 100644 --- a/swift/extractor/visitors/TypeVisitor.h +++ b/swift/extractor/visitors/TypeVisitor.h @@ -9,6 +9,8 @@ class TypeVisitor : public TypeVisitorBase { using TypeVisitorBase::TypeVisitorBase; void visit(swift::TypeBase* type); + codeql::TypeRepr translateTypeRepr(const swift::TypeRepr& typeRepr, swift::Type type); + void visitProtocolType(swift::ProtocolType* type); void visitEnumType(swift::EnumType* type); void visitStructType(swift::StructType* type); @@ -68,6 +70,8 @@ class TypeVisitor : public TypeVisitorBase { codeql::BuiltinUnsafeValueBufferType translateBuiltinUnsafeValueBufferType( const swift::BuiltinUnsafeValueBufferType& type); codeql::BuiltinVectorType translateBuiltinVectorType(const swift::BuiltinVectorType& type); + codeql::OpenedArchetypeType translateOpenedArchetypeType(const swift::OpenedArchetypeType& type); + codeql::ModuleType translateModuleType(const swift::ModuleType& type); private: void fillType(const swift::TypeBase& type, codeql::Type& entry); @@ -80,9 +84,9 @@ class TypeVisitor : public TypeVisitorBase { void emitBoundGenericType(swift::BoundGenericType* type, TrapLabel label); void emitAnyGenericType(swift::AnyGenericType* type, TrapLabel label); - template - auto createEntry(const T& type) { - TrapClassOf entry{dispatcher_.assignNewLabel(type)}; + template + auto createTypeEntry(const T& type, const Args&... args) { + auto entry = dispatcher_.createEntry(type, args...); fillType(type, entry); return entry; } diff --git a/swift/extractor/visitors/VisitorBase.h b/swift/extractor/visitors/VisitorBase.h index 8402e8924af..b835492d00d 100644 --- a/swift/extractor/visitors/VisitorBase.h +++ b/swift/extractor/visitors/VisitorBase.h @@ -29,7 +29,7 @@ class VisitorBase { public: \ void visit##CLASS##KIND(swift::CLASS##KIND* e) { \ using TranslateResult = std::invoke_result_t; \ + CrtpSubclass, swift::CLASS##KIND&>; \ constexpr bool hasTranslateImplementation = !std::is_same_v; \ if constexpr (hasTranslateImplementation) { \ dispatcher_.emit(static_cast(this)->translate##CLASS##KIND(*e)); \ diff --git a/swift/integration-tests/.gitignore b/swift/integration-tests/.gitignore index 8e66c817556..9ea4244ad91 100644 --- a/swift/integration-tests/.gitignore +++ b/swift/integration-tests/.gitignore @@ -6,3 +6,4 @@ xcuserdata/ DerivedData/ .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata *.actual +db diff --git a/swift/integration-tests/create_database_utils.py b/swift/integration-tests/create_database_utils.py new file mode 100644 index 00000000000..38822fd70b4 --- /dev/null +++ b/swift/integration-tests/create_database_utils.py @@ -0,0 +1,26 @@ +""" +recreation of internal `create_database_utils.py` to run the tests locally, with minimal +and swift-specialized functionality +""" +import subprocess +import pathlib +import sys + + +def run_codeql_database_create(cmds, lang, keep_trap=True): + assert lang == 'swift' + codeql_root = pathlib.Path(__file__).parents[2] + cmd = [ + "codeql", "database", "create", + "-s", ".", "-l", "swift", "--internal-use-lua-tracing", f"--search-path={codeql_root}", "--no-cleanup", + ] + if keep_trap: + cmd.append("--keep-trap") + for c in cmds: + cmd += ["-c", c] + cmd.append("db") + res = subprocess.run(cmd) + if res.returncode: + print("FAILED", file=sys.stderr) + print(" ", *cmd, file=sys.stderr) + sys.exit(res.returncode) diff --git a/swift/integration-tests/cross-references/Functions.expected b/swift/integration-tests/cross-references/Functions.expected deleted file mode 100644 index 76335112240..00000000000 --- a/swift/integration-tests/cross-references/Functions.expected +++ /dev/null @@ -1,4 +0,0 @@ -| Sources/cross-references/lib.swift:1:1:1:11 | f | -| Sources/cross-references/lib.swift:17:8:19:1 | ~ | -| Sources/cross-references/lib.swift:22:9:24:1 | ~ | -| Sources/cross-references/lib.swift:27:1:29:1 | ~ | diff --git a/swift/integration-tests/frontend-invocations/C.swift b/swift/integration-tests/osx-only/frontend-invocations/A.swift similarity index 100% rename from swift/integration-tests/frontend-invocations/C.swift rename to swift/integration-tests/osx-only/frontend-invocations/A.swift diff --git a/swift/integration-tests/frontend-invocations/D.swift b/swift/integration-tests/osx-only/frontend-invocations/B.swift similarity index 100% rename from swift/integration-tests/frontend-invocations/D.swift rename to swift/integration-tests/osx-only/frontend-invocations/B.swift diff --git a/swift/integration-tests/frontend-invocations/E.swift b/swift/integration-tests/osx-only/frontend-invocations/C.swift similarity index 100% rename from swift/integration-tests/frontend-invocations/E.swift rename to swift/integration-tests/osx-only/frontend-invocations/C.swift diff --git a/swift/integration-tests/frontend-invocations/Esup.swift b/swift/integration-tests/osx-only/frontend-invocations/D.swift similarity index 100% rename from swift/integration-tests/frontend-invocations/Esup.swift rename to swift/integration-tests/osx-only/frontend-invocations/D.swift diff --git a/swift/integration-tests/partial-modules/Unknown.expected b/swift/integration-tests/osx-only/frontend-invocations/E.swift similarity index 100% rename from swift/integration-tests/partial-modules/Unknown.expected rename to swift/integration-tests/osx-only/frontend-invocations/E.swift diff --git a/swift/ql/test/extractor-tests/generated/File/hello.swift b/swift/integration-tests/osx-only/frontend-invocations/Esup.swift similarity index 100% rename from swift/ql/test/extractor-tests/generated/File/hello.swift rename to swift/integration-tests/osx-only/frontend-invocations/Esup.swift diff --git a/swift/integration-tests/frontend-invocations/Files.expected b/swift/integration-tests/osx-only/frontend-invocations/Files.expected similarity index 100% rename from swift/integration-tests/frontend-invocations/Files.expected rename to swift/integration-tests/osx-only/frontend-invocations/Files.expected diff --git a/swift/integration-tests/frontend-invocations/Files.ql b/swift/integration-tests/osx-only/frontend-invocations/Files.ql similarity index 100% rename from swift/integration-tests/frontend-invocations/Files.ql rename to swift/integration-tests/osx-only/frontend-invocations/Files.ql diff --git a/swift/integration-tests/frontend-invocations/Makefile b/swift/integration-tests/osx-only/frontend-invocations/Makefile similarity index 100% rename from swift/integration-tests/frontend-invocations/Makefile rename to swift/integration-tests/osx-only/frontend-invocations/Makefile diff --git a/swift/integration-tests/frontend-invocations/test.py b/swift/integration-tests/osx-only/frontend-invocations/test.py similarity index 100% rename from swift/integration-tests/frontend-invocations/test.py rename to swift/integration-tests/osx-only/frontend-invocations/test.py diff --git a/swift/integration-tests/cross-references/Classes.expected b/swift/integration-tests/posix-only/cross-references/Classes.expected similarity index 100% rename from swift/integration-tests/cross-references/Classes.expected rename to swift/integration-tests/posix-only/cross-references/Classes.expected diff --git a/swift/integration-tests/cross-references/Classes.ql b/swift/integration-tests/posix-only/cross-references/Classes.ql similarity index 100% rename from swift/integration-tests/cross-references/Classes.ql rename to swift/integration-tests/posix-only/cross-references/Classes.ql diff --git a/swift/integration-tests/cross-references/Constructors.expected b/swift/integration-tests/posix-only/cross-references/Constructors.expected similarity index 100% rename from swift/integration-tests/cross-references/Constructors.expected rename to swift/integration-tests/posix-only/cross-references/Constructors.expected diff --git a/swift/integration-tests/cross-references/Constructors.ql b/swift/integration-tests/posix-only/cross-references/Constructors.ql similarity index 100% rename from swift/integration-tests/cross-references/Constructors.ql rename to swift/integration-tests/posix-only/cross-references/Constructors.ql diff --git a/swift/integration-tests/cross-references/Destructors.expected b/swift/integration-tests/posix-only/cross-references/Destructors.expected similarity index 100% rename from swift/integration-tests/cross-references/Destructors.expected rename to swift/integration-tests/posix-only/cross-references/Destructors.expected diff --git a/swift/integration-tests/cross-references/Destructors.ql b/swift/integration-tests/posix-only/cross-references/Destructors.ql similarity index 100% rename from swift/integration-tests/cross-references/Destructors.ql rename to swift/integration-tests/posix-only/cross-references/Destructors.ql diff --git a/swift/integration-tests/cross-references/Enums.expected b/swift/integration-tests/posix-only/cross-references/Enums.expected similarity index 100% rename from swift/integration-tests/cross-references/Enums.expected rename to swift/integration-tests/posix-only/cross-references/Enums.expected diff --git a/swift/integration-tests/cross-references/Enums.ql b/swift/integration-tests/posix-only/cross-references/Enums.ql similarity index 100% rename from swift/integration-tests/cross-references/Enums.ql rename to swift/integration-tests/posix-only/cross-references/Enums.ql diff --git a/swift/integration-tests/posix-only/cross-references/Functions.expected b/swift/integration-tests/posix-only/cross-references/Functions.expected new file mode 100644 index 00000000000..2ecf78a6be4 --- /dev/null +++ b/swift/integration-tests/posix-only/cross-references/Functions.expected @@ -0,0 +1,4 @@ +| Sources/cross-references/lib.swift:1:1:1:11 | f() | +| Sources/cross-references/lib.swift:17:8:19:1 | ~(_:) | +| Sources/cross-references/lib.swift:22:9:24:1 | ~(_:) | +| Sources/cross-references/lib.swift:27:1:29:1 | ~(_:_:) | diff --git a/swift/integration-tests/cross-references/Functions.ql b/swift/integration-tests/posix-only/cross-references/Functions.ql similarity index 100% rename from swift/integration-tests/cross-references/Functions.ql rename to swift/integration-tests/posix-only/cross-references/Functions.ql diff --git a/swift/integration-tests/cross-references/Operators.expected b/swift/integration-tests/posix-only/cross-references/Operators.expected similarity index 100% rename from swift/integration-tests/cross-references/Operators.expected rename to swift/integration-tests/posix-only/cross-references/Operators.expected diff --git a/swift/integration-tests/cross-references/Operators.ql b/swift/integration-tests/posix-only/cross-references/Operators.ql similarity index 100% rename from swift/integration-tests/cross-references/Operators.ql rename to swift/integration-tests/posix-only/cross-references/Operators.ql diff --git a/swift/integration-tests/cross-references/Package.swift b/swift/integration-tests/posix-only/cross-references/Package.swift similarity index 100% rename from swift/integration-tests/cross-references/Package.swift rename to swift/integration-tests/posix-only/cross-references/Package.swift diff --git a/swift/integration-tests/cross-references/Protocols.expected b/swift/integration-tests/posix-only/cross-references/Protocols.expected similarity index 100% rename from swift/integration-tests/cross-references/Protocols.expected rename to swift/integration-tests/posix-only/cross-references/Protocols.expected diff --git a/swift/integration-tests/cross-references/Protocols.ql b/swift/integration-tests/posix-only/cross-references/Protocols.ql similarity index 100% rename from swift/integration-tests/cross-references/Protocols.ql rename to swift/integration-tests/posix-only/cross-references/Protocols.ql diff --git a/swift/integration-tests/cross-references/Sources/cross-references/lib.swift b/swift/integration-tests/posix-only/cross-references/Sources/cross-references/lib.swift similarity index 99% rename from swift/integration-tests/cross-references/Sources/cross-references/lib.swift rename to swift/integration-tests/posix-only/cross-references/Sources/cross-references/lib.swift index 39acad28b14..1155935d326 100644 --- a/swift/integration-tests/cross-references/Sources/cross-references/lib.swift +++ b/swift/integration-tests/posix-only/cross-references/Sources/cross-references/lib.swift @@ -27,4 +27,3 @@ infix operator ~ func ~(lhs: Int, rhs: Int) -> Int { return lhs } - diff --git a/swift/integration-tests/cross-references/Sources/cross-references/main.swift b/swift/integration-tests/posix-only/cross-references/Sources/cross-references/main.swift similarity index 98% rename from swift/integration-tests/cross-references/Sources/cross-references/main.swift rename to swift/integration-tests/posix-only/cross-references/Sources/cross-references/main.swift index b4143493c41..474d885af58 100644 --- a/swift/integration-tests/cross-references/Sources/cross-references/main.swift +++ b/swift/integration-tests/posix-only/cross-references/Sources/cross-references/main.swift @@ -15,4 +15,3 @@ struct s : P {} 42~ 15 ~ 42 - diff --git a/swift/integration-tests/cross-references/Structs.expected b/swift/integration-tests/posix-only/cross-references/Structs.expected similarity index 100% rename from swift/integration-tests/cross-references/Structs.expected rename to swift/integration-tests/posix-only/cross-references/Structs.expected diff --git a/swift/integration-tests/cross-references/Structs.ql b/swift/integration-tests/posix-only/cross-references/Structs.ql similarity index 100% rename from swift/integration-tests/cross-references/Structs.ql rename to swift/integration-tests/posix-only/cross-references/Structs.ql diff --git a/swift/integration-tests/cross-references/VarDecls.expected b/swift/integration-tests/posix-only/cross-references/VarDecls.expected similarity index 83% rename from swift/integration-tests/cross-references/VarDecls.expected rename to swift/integration-tests/posix-only/cross-references/VarDecls.expected index 104271d5384..6fab086e7dd 100644 --- a/swift/integration-tests/cross-references/VarDecls.expected +++ b/swift/integration-tests/posix-only/cross-references/VarDecls.expected @@ -1,5 +1,4 @@ | Sources/cross-references/lib.swift:10:5:10:5 | X | -| Sources/cross-references/lib.swift:10:5:10:5 | X | | Sources/cross-references/lib.swift:17:16:17:19 | v | | Sources/cross-references/lib.swift:22:16:22:19 | v | | Sources/cross-references/lib.swift:27:8:27:13 | lhs | diff --git a/swift/integration-tests/cross-references/VarDecls.ql b/swift/integration-tests/posix-only/cross-references/VarDecls.ql similarity index 100% rename from swift/integration-tests/cross-references/VarDecls.ql rename to swift/integration-tests/posix-only/cross-references/VarDecls.ql diff --git a/swift/integration-tests/cross-references/test.py b/swift/integration-tests/posix-only/cross-references/test.py similarity index 100% rename from swift/integration-tests/cross-references/test.py rename to swift/integration-tests/posix-only/cross-references/test.py diff --git a/swift/integration-tests/hello-world/Package.swift b/swift/integration-tests/posix-only/hello-world/Package.swift similarity index 100% rename from swift/integration-tests/hello-world/Package.swift rename to swift/integration-tests/posix-only/hello-world/Package.swift diff --git a/swift/integration-tests/hello-world/Sources/hello-world/hello_world.swift b/swift/integration-tests/posix-only/hello-world/Sources/hello-world/hello_world.swift similarity index 100% rename from swift/integration-tests/hello-world/Sources/hello-world/hello_world.swift rename to swift/integration-tests/posix-only/hello-world/Sources/hello-world/hello_world.swift diff --git a/swift/integration-tests/hello-world/test.expected b/swift/integration-tests/posix-only/hello-world/test.expected similarity index 100% rename from swift/integration-tests/hello-world/test.expected rename to swift/integration-tests/posix-only/hello-world/test.expected diff --git a/swift/integration-tests/hello-world/test.py b/swift/integration-tests/posix-only/hello-world/test.py similarity index 100% rename from swift/integration-tests/hello-world/test.py rename to swift/integration-tests/posix-only/hello-world/test.py diff --git a/swift/integration-tests/hello-world/test.ql b/swift/integration-tests/posix-only/hello-world/test.ql similarity index 100% rename from swift/integration-tests/hello-world/test.ql rename to swift/integration-tests/posix-only/hello-world/test.ql diff --git a/swift/integration-tests/partial-modules/A/Package.swift b/swift/integration-tests/posix-only/partial-modules/A/Package.swift similarity index 100% rename from swift/integration-tests/partial-modules/A/Package.swift rename to swift/integration-tests/posix-only/partial-modules/A/Package.swift diff --git a/swift/integration-tests/partial-modules/A/Sources/A/A.swift b/swift/integration-tests/posix-only/partial-modules/A/Sources/A/A.swift similarity index 100% rename from swift/integration-tests/partial-modules/A/Sources/A/A.swift rename to swift/integration-tests/posix-only/partial-modules/A/Sources/A/A.swift diff --git a/swift/integration-tests/partial-modules/A/Sources/A/Asup.swift b/swift/integration-tests/posix-only/partial-modules/A/Sources/A/Asup.swift similarity index 100% rename from swift/integration-tests/partial-modules/A/Sources/A/Asup.swift rename to swift/integration-tests/posix-only/partial-modules/A/Sources/A/Asup.swift diff --git a/swift/integration-tests/partial-modules/B/Package.swift b/swift/integration-tests/posix-only/partial-modules/B/Package.swift similarity index 100% rename from swift/integration-tests/partial-modules/B/Package.swift rename to swift/integration-tests/posix-only/partial-modules/B/Package.swift diff --git a/swift/integration-tests/partial-modules/B/Sources/B/B.swift b/swift/integration-tests/posix-only/partial-modules/B/Sources/B/B.swift similarity index 100% rename from swift/integration-tests/partial-modules/B/Sources/B/B.swift rename to swift/integration-tests/posix-only/partial-modules/B/Sources/B/B.swift diff --git a/swift/integration-tests/partial-modules/B/Sources/B/Bsup.swift b/swift/integration-tests/posix-only/partial-modules/B/Sources/B/Bsup.swift similarity index 100% rename from swift/integration-tests/partial-modules/B/Sources/B/Bsup.swift rename to swift/integration-tests/posix-only/partial-modules/B/Sources/B/Bsup.swift diff --git a/swift/integration-tests/posix-only/partial-modules/Modules.expected b/swift/integration-tests/posix-only/partial-modules/Modules.expected new file mode 100644 index 00000000000..3cdcff9b980 --- /dev/null +++ b/swift/integration-tests/posix-only/partial-modules/Modules.expected @@ -0,0 +1,5 @@ +| file://:0:0:0:0 | A | +| file://:0:0:0:0 | B | +| file://:0:0:0:0 | PackageDescription | +| file://:0:0:0:0 | main | +| file://:0:0:0:0 | partial_modules | diff --git a/swift/integration-tests/posix-only/partial-modules/Modules.ql b/swift/integration-tests/posix-only/partial-modules/Modules.ql new file mode 100644 index 00000000000..d456e261a3c --- /dev/null +++ b/swift/integration-tests/posix-only/partial-modules/Modules.ql @@ -0,0 +1,5 @@ +import swift + +from ModuleDecl decl +where not decl.isBuiltinModule() and not decl.isSystemModule() +select decl diff --git a/swift/integration-tests/partial-modules/Package.swift b/swift/integration-tests/posix-only/partial-modules/Package.swift similarity index 100% rename from swift/integration-tests/partial-modules/Package.swift rename to swift/integration-tests/posix-only/partial-modules/Package.swift diff --git a/swift/integration-tests/partial-modules/Sources/partial-modules/partial_modules.swift b/swift/integration-tests/posix-only/partial-modules/Sources/partial-modules/partial_modules.swift similarity index 100% rename from swift/integration-tests/partial-modules/Sources/partial-modules/partial_modules.swift rename to swift/integration-tests/posix-only/partial-modules/Sources/partial-modules/partial_modules.swift diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.expected b/swift/integration-tests/posix-only/partial-modules/Unknown.expected similarity index 100% rename from swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.expected rename to swift/integration-tests/posix-only/partial-modules/Unknown.expected diff --git a/swift/integration-tests/partial-modules/Unknown.ql b/swift/integration-tests/posix-only/partial-modules/Unknown.ql similarity index 100% rename from swift/integration-tests/partial-modules/Unknown.ql rename to swift/integration-tests/posix-only/partial-modules/Unknown.ql diff --git a/swift/integration-tests/partial-modules/test.py b/swift/integration-tests/posix-only/partial-modules/test.py similarity index 100% rename from swift/integration-tests/partial-modules/test.py rename to swift/integration-tests/posix-only/partial-modules/test.py diff --git a/swift/integration-tests/runner.py b/swift/integration-tests/runner.py new file mode 100755 index 00000000000..77a62bfb81b --- /dev/null +++ b/swift/integration-tests/runner.py @@ -0,0 +1,82 @@ +#!/bin/env python3 +""" +recreation of internal `integration-tests-runner.py` to run the tests locally, with minimal +and swift-specialized functionality. + +This runner requires: +* a codeql CLI binary in PATH +* `bazel run //swift:create_extractor_pack` to have been run +""" + +import pathlib +import os +import sys +import subprocess +import argparse +import shutil +import platform + +this_dir = pathlib.Path(__file__).parent + + +def options(): + p = argparse.ArgumentParser() + p.add_argument("--test-dir", "-d", type=pathlib.Path, action="append") + #FIXME: the following should be the default + p.add_argument("--check-databases", action="store_true") + p.add_argument("--learn", action="store_true") + p.add_argument("--threads", "-j", type=int, default=0) + return p.parse_args() + + +def execute_test(path): + shutil.rmtree(path.parent / "db", ignore_errors=True) + return subprocess.run([sys.executable, "-u", path.name], cwd=path.parent).returncode == 0 + +def skipped(test): + return platform.system() != "Darwin" and "osx-only" in test.parts + + +def main(opts): + test_dirs = opts.test_dir or [this_dir] + tests = [t for d in test_dirs for t in d.rglob("test.py") if not skipped(t)] + + if not tests: + print("No tests found", file=sys.stderr) + return False + + os.environ["PYTHONPATH"] = str(this_dir) + failed_db_creation = [] + succesful_db_creation = [] + for t in tests: + (succesful_db_creation if execute_test(t) else failed_db_creation).append(t) + + if succesful_db_creation: + codeql_root = this_dir.parents[1] + cmd = [ + "codeql", "test", "run", + f"--search-path={codeql_root}", + "--keep-databases", + "--dataset=db/db-swift", + f"--threads={opts.threads}", + ] + if opts.check_databases: + cmd.append("--check-databases") + else: + cmd.append("--no-check-databases") + if opts.learn: + cmd.append("--learn") + cmd.extend(str(t.parent) for t in succesful_db_creation) + ql_test_success = subprocess.run(cmd).returncode == 0 + + if failed_db_creation: + print("Database creation failed:", file=sys.stderr) + for t in failed_db_creation: + print(" ", t.parent, file=sys.stderr) + return False + + return ql_test_success + + +if __name__ == "__main__": + sys.exit(0 if main(options()) else 1) diff --git a/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll b/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll index 27af5b1c789..5358a35999f 100644 --- a/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll +++ b/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll @@ -125,6 +125,8 @@ class ApplyExprCfgNode extends ExprCfgNode { } AbstractFunctionDecl getStaticTarget() { result = e.getStaticTarget() } + + Expr getFunction() { result = e.getFunction() } } class CallExprCfgNode extends ApplyExprCfgNode { diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll index b9d3cc2a6ec..2b7e7b5b963 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll @@ -59,7 +59,7 @@ module CfgScope { private class KeyPathScope extends Range_ instanceof KeyPathExpr { AstControlFlowTree tree; - KeyPathScope() { tree.getAst() = this.getParsedRoot().getFullyConverted() } + KeyPathScope() { tree.getAst() = this } final override predicate entry(ControlFlowElement first) { first(tree, first) } @@ -96,21 +96,31 @@ module Stmts { override predicate propagatesAbnormal(ControlFlowElement node) { none() } + private predicate isBodyOfTapExpr() { any(TapExpr tap).getBody() = ast } + + // Note: If the brace statement is the body of a `TapExpr`, the first element is the variable + // declaration (see https://github.com/apple/swift/blob/main/include/swift/AST/Expr.h#L848) + // that's initialized by the `TapExpr`. In `TapExprTree` we've already visited this declaration, + // along with its initializer. So we skip the first element here. + private AstNode getFirstElement() { + if this.isBodyOfTapExpr() then result = ast.getElement(1) else result = ast.getFirstElement() + } + override predicate first(ControlFlowElement first) { this.firstInner(first) or - not exists(ast.getFirstElement()) and first.asAstNode() = ast + not exists(this.getFirstElement()) and first.asAstNode() = ast } override predicate last(ControlFlowElement last, Completion c) { this.lastInner(last, c) or - not exists(ast.getFirstElement()) and + not exists(this.getFirstElement()) and last.asAstNode() = ast and c instanceof SimpleCompletion } - predicate firstInner(ControlFlowElement first) { astFirst(ast.getFirstElement(), first) } + predicate firstInner(ControlFlowElement first) { astFirst(this.getFirstElement(), first) } /** Gets the body of the i'th `defer` statement. */ private BraceStmt getDeferStmtBody(int i) { @@ -836,9 +846,6 @@ module Patterns { // Note: `getSubPattern` only has a result if the `is` pattern is of the form `pattern as type`. i = 0 and result.asAstNode() = ast.getSubPattern().getFullyUnresolved() - or - i = 1 and - result.asAstNode() = ast.getCastTypeRepr() } } @@ -1337,10 +1344,38 @@ module Exprs { override InterpolatedStringLiteralExpr ast; final override ControlFlowElement getChildElement(int i) { - none() // TODO + i = 0 and + result.asAstNode() = ast.getAppendingExpr().getFullyConverted() } } + /** Control-flow for a `TapExpr`. See the QLDoc for `TapExpr` for the semantics of a `TapExpr`. */ + private class TapExprTree extends AstStandardPostOrderTree { + override TapExpr ast; + + final override ControlFlowElement getChildElement(int i) { + // We first visit the local variable declaration. + i = 0 and + result.asAstNode() = ast.getVar() + or + // Then we visit the expression that gives the local variable its initial value. + i = 1 and + result.asAstNode() = ast.getSubExpr().getFullyConverted() + or + // And finally, we visit the body that potentially mutates the local variable. + // Note that the CFG for the body will skip the first element in the + // body because it's guarenteed to be the variable declaration + // that we've already visited at i = 0. See the explanation + // in `BraceStmtTree` for why this is necessary. + i = 2 and + result.asAstNode() = ast.getBody() + } + } + + private class OpaqueValueExprTree extends AstLeafTree { + override OpaqueValueExpr ast; + } + module DeclRefExprs { class DeclRefExprLValueTree extends AstLeafTree { override DeclRefExpr ast; @@ -1604,8 +1639,14 @@ module Exprs { final override ControlFlowElement getChildElement(int i) { result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 - or - result.asAstNode() = ast.getTypeRepr().getFullyUnresolved() and i = 1 + } + } + + private class IsTree extends AstStandardPostOrderTree { + override IsExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 } } @@ -1635,8 +1676,8 @@ module Exprs { } } - private class TryTree extends AstStandardPostOrderTree { - override TryExpr ast; + private class AnyTryTree extends AstStandardPostOrderTree { + override AnyTryExpr ast; override ControlFlowElement getChildElement(int i) { i = 0 and diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll index 47fcd24883c..7d0dd10c084 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll +++ b/swift/ql/lib/codeql/swift/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/swift/ql/lib/codeql/swift/dataflow/FlowSources.qll b/swift/ql/lib/codeql/swift/dataflow/FlowSources.qll index 4ef8a28baa3..dc0d692dfc4 100644 --- a/swift/ql/lib/codeql/swift/dataflow/FlowSources.qll +++ b/swift/ql/lib/codeql/swift/dataflow/FlowSources.qll @@ -11,6 +11,9 @@ abstract class RemoteFlowSource extends Node { abstract string getSourceType(); } +/** + * A data flow source of remote user input that is defined through 'models as data'. + */ private class ExternalRemoteFlowSource extends RemoteFlowSource { ExternalRemoteFlowSource() { sourceNode(this, "remote") } diff --git a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll index 2805dff4637..53d995f4873 100644 --- a/swift/ql/lib/codeql/swift/dataflow/Ssa.qll +++ b/swift/ql/lib/codeql/swift/dataflow/Ssa.qll @@ -39,6 +39,11 @@ module Ssa { read2 = bb2.getNode(i2) ) } + + cached + predicate lastRefRedef(BasicBlock bb, int i, Definition next) { + SsaImplCommon::lastRefRedef(this, bb, i, next) + } } cached @@ -75,7 +80,7 @@ module Ssa { cached predicate isInoutDef(ExprCfgNode argument) { exists( - CallExpr c, BasicBlock bb, int blockIndex, int argIndex, VarDecl v, InOutExpr argExpr // TODO: use CFG node for assignment expr + ApplyExpr c, BasicBlock bb, int blockIndex, int argIndex, VarDecl v, InOutExpr argExpr // TODO: use CFG node for assignment expr | this.definesAt(v, bb, blockIndex) and bb.getNode(blockIndex).getNode().asAstNode() = c and diff --git a/swift/ql/lib/codeql/swift/dataflow/TaintTracking.qll b/swift/ql/lib/codeql/swift/dataflow/TaintTracking.qll new file mode 100644 index 00000000000..b554424211f --- /dev/null +++ b/swift/ql/lib/codeql/swift/dataflow/TaintTracking.qll @@ -0,0 +1,7 @@ +/** + * Provides classes for performing local (intra-procedural) and + * global (inter-procedural) taint-tracking analyses. + */ +module TaintTracking { + import codeql.swift.dataflow.internal.tainttracking1.TaintTrackingImpl +} diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll index a076f7a2e45..340bfe280b7 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl.qll @@ -597,7 +597,7 @@ private predicate hasSinkCallCtx(Configuration config) { ) } -private module Stage1 { +private module Stage1 implements StageSig { class ApApprox = Unit; class Ap = Unit; @@ -944,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) } @@ -1142,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 ) { @@ -1221,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) { @@ -1235,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 @@ -1248,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] @@ -1883,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)) } /** @@ -1967,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; @@ -1994,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) { @@ -2067,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 @@ -2080,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 } /** @@ -2644,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 @@ -2828,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; @@ -2855,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 ) { @@ -2894,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)) @@ -3495,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) ) } @@ -3553,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) ) } @@ -3667,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) { @@ -4207,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) } /** @@ -4243,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/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll index e530df2fc20..c1984e0b80f 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll @@ -5,7 +5,6 @@ private import codeql.swift.controlflow.ControlFlowGraph private import codeql.swift.controlflow.CfgNodes private import codeql.swift.dataflow.Ssa private import codeql.swift.controlflow.BasicBlocks -private import codeql.swift.dataflow.internal.SsaImplCommon as SsaImpl private import codeql.swift.dataflow.FlowSummary as FlowSummary private import codeql.swift.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl @@ -51,7 +50,7 @@ private class SsaDefinitionNodeImpl extends SsaDefinitionNode, NodeImpl { } private predicate localFlowSsaInput(Node nodeFrom, Ssa::Definition def, Ssa::Definition next) { - exists(BasicBlock bb, int i | SsaImpl::lastRefRedef(def, bb, i, next) | + exists(BasicBlock bb, int i | def.lastRefRedef(bb, i, next) | def.definesAt(_, bb, i) and def = nodeFrom.asDefinition() ) @@ -65,10 +64,7 @@ private module Cached { TExprNode(CfgNode n, Expr e) { hasExprNode(n, e) } or TSsaDefinitionNode(Ssa::Definition def) or TInoutReturnNode(ParamDecl param) { param.isInout() } or - TInOutUpdateNode(ParamDecl param, CallExpr call) { - param.isInout() and - call.getStaticTarget() = param.getDeclaringFunction() - } or + TInOutUpdateNode(Argument arg) { arg.getExpr() instanceof InOutExpr } or TSummaryNode(FlowSummary::SummarizedCallable c, FlowSummaryImpl::Private::SummaryNodeState state) private predicate hasExprNode(CfgNode n, Expr e) { @@ -203,7 +199,7 @@ abstract class ArgumentNode extends Node { private module ArgumentNodes { class NormalArgumentNode extends ExprNode, ArgumentNode { - NormalArgumentNode() { exists(CallExpr call | call.getAnArgument().getExpr() = this.asExpr()) } + NormalArgumentNode() { exists(ApplyExpr call | call.getAnArgument().getExpr() = this.asExpr()) } override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { call.asCall().getArgument(pos.(PositionalArgumentPosition).getIndex()).getExpr() = @@ -281,23 +277,22 @@ private module OutNodes { } class InOutUpdateNode extends OutNode, TInOutUpdateNode, NodeImpl { - ParamDecl param; - CallExpr call; + Argument arg; - InOutUpdateNode() { this = TInOutUpdateNode(param, call) } + InOutUpdateNode() { this = TInOutUpdateNode(arg) } override DataFlowCall getCall(ReturnKind kind) { - result.asCall().getExpr() = call and - kind.(ParamReturnKind).getIndex() = param.getIndex() + result.asCall().getExpr() = arg.getApplyExpr() and + kind.(ParamReturnKind).getIndex() = arg.getIndex() } override DataFlowCallable getEnclosingCallable() { result = this.getCall(_).getEnclosingCallable() } - override Location getLocationImpl() { result = call.getLocation() } + override Location getLocationImpl() { result = arg.getLocation() } - override string toStringImpl() { result = param.toString() } + override string toStringImpl() { result = arg.toString() } } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll index 5bb0aa9750e..79288e648e6 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/FlowSummaryImplSpecific.qll @@ -11,6 +11,7 @@ private import FlowSummaryImpl::Private private import FlowSummaryImpl::Public private import codeql.swift.dataflow.ExternalFlow private import codeql.swift.dataflow.FlowSummary as FlowSummary +private import codeql.swift.controlflow.CfgNodes class SummarizedCallableBase = AbstractFunctionDecl; @@ -153,7 +154,9 @@ class InterpretNode extends TInterpretNode { DataFlowCallable asCallable() { result.getUnderlyingCallable() = this.asElement() } /** Gets the target of this call, if any. */ - AbstractFunctionDecl getCallTarget() { result = this.asCall().asCall().getStaticTarget() } + AbstractFunctionDecl getCallTarget() { + result = this.asCall().asCall().getFunction().(ApplyExpr).getStaticTarget() + } /** Gets a textual representation of this node. */ string toString() { diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll b/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll index 687e3b2cef8..2f507b31645 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/SsaImplSpecific.qll @@ -29,7 +29,7 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) certain = true ) or - exists(CallExpr call, Argument arg | + exists(ApplyExpr call, Argument arg | arg.getExpr().(InOutExpr).getSubExpr() = v.getAnAccess() and call.getAnArgument() = arg and bb.getNode(i).getNode().asAstNode() = call and @@ -39,6 +39,13 @@ predicate variableWrite(BasicBlock bb, int i, SourceVariable v, boolean certain) v instanceof ParamDecl and bb.getNode(i).getNode().asAstNode() = v and certain = true + or + // Mark the subexpression as a write of the local variable declared in the `TapExpr`. + exists(TapExpr tap | + v = tap.getVar() and + bb.getNode(i).getNode().asAstNode() = tap.getSubExpr() and + certain = true + ) } private predicate isLValue(DeclRefExpr ref) { any(AssignExpr assign).getDest() = ref } @@ -58,4 +65,11 @@ predicate variableRead(BasicBlock bb, int i, SourceVariable v, boolean certain) bb.getScope() = func and certain = true ) + or + // Mark the `TapExpr` as a read of the of the local variable. + exists(TapExpr tap | + v = tap.getVar() and + bb.getNode(i).getNode().asAstNode() = tap and + certain = true + ) } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll index 17cf1305ea1..58e29b61bba 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll @@ -2,6 +2,8 @@ private import swift private import DataFlowPrivate private import TaintTrackingPublic private import codeql.swift.dataflow.DataFlow +private import codeql.swift.dataflow.Ssa +private import codeql.swift.controlflow.CfgNodes /** * Holds if `node` should be a sanitizer in all global taint flow configurations @@ -16,7 +18,30 @@ private module Cached { * in all global taint flow configurations. */ cached - predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { none() } + predicate defaultAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + // Flow through one argument of `appendLiteral` and `appendInterpolation` and to the second argument. + // This is needed for string interpolation generated by the compiler. An interpolated string + // like `"I am \(n) years old."` is represented as + // ``` + // $interpolated = "" + // appendLiteral(&$interpolated, "I am ") + // appendInterpolation(&$interpolated, n) + // appendLiteral(&$interpolated, " years old.") + // ``` + exists(ApplyExpr apply1, ApplyExpr apply2, ExprCfgNode e | + nodeFrom.asExpr() = [apply1, apply2].getAnArgument().getExpr() and + apply1.getFunction() = apply2 and + apply2.getStaticTarget().getName() = ["appendLiteral(_:)", "appendInterpolation(_:)"] and + e.getExpr() = apply2.getAnArgument().getExpr() and + nodeTo.asDefinition().(Ssa::WriteDefinition).isInoutDef(e) + ) + or + // Flow from the computation of the interpolated string literal to the result of the interpolation. + exists(InterpolatedStringLiteralExpr interpolated | + nodeTo.asExpr() = interpolated and + nodeFrom.asExpr() = interpolated.getAppendingExpr() + ) + } /** * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local diff --git a/swift/ql/lib/codeql/swift/elements.qll b/swift/ql/lib/codeql/swift/elements.qll index c10706d7c7a..7469e597a4b 100644 --- a/swift/ql/lib/codeql/swift/elements.qll +++ b/swift/ql/lib/codeql/swift/elements.qll @@ -1,6 +1,7 @@ // generated by codegen/codegen.py import codeql.swift.elements.AstNode import codeql.swift.elements.Callable +import codeql.swift.elements.Comment import codeql.swift.elements.Element import codeql.swift.elements.File import codeql.swift.elements.Locatable @@ -24,6 +25,7 @@ import codeql.swift.elements.decl.FuncDecl import codeql.swift.elements.decl.GenericContext import codeql.swift.elements.decl.GenericTypeDecl import codeql.swift.elements.decl.GenericTypeParamDecl +import codeql.swift.elements.decl.IfConfigClause import codeql.swift.elements.decl.IfConfigDecl import codeql.swift.elements.decl.ImportDecl import codeql.swift.elements.decl.InfixOperatorDecl @@ -271,6 +273,7 @@ import codeql.swift.elements.type.SyntaxSugarType import codeql.swift.elements.type.TupleType import codeql.swift.elements.type.Type import codeql.swift.elements.type.TypeAliasType +import codeql.swift.elements.type.TypeRepr import codeql.swift.elements.type.TypeVariableType import codeql.swift.elements.type.UnarySyntaxSugarType import codeql.swift.elements.type.UnboundGenericType @@ -279,32 +282,3 @@ import codeql.swift.elements.type.UnownedStorageType import codeql.swift.elements.type.UnresolvedType import codeql.swift.elements.type.VariadicSequenceType import codeql.swift.elements.type.WeakStorageType -import codeql.swift.elements.typerepr.ArrayTypeRepr -import codeql.swift.elements.typerepr.AttributedTypeRepr -import codeql.swift.elements.typerepr.CompileTimeConstTypeRepr -import codeql.swift.elements.typerepr.ComponentIdentTypeRepr -import codeql.swift.elements.typerepr.CompositionTypeRepr -import codeql.swift.elements.typerepr.CompoundIdentTypeRepr -import codeql.swift.elements.typerepr.DictionaryTypeRepr -import codeql.swift.elements.typerepr.ErrorTypeRepr -import codeql.swift.elements.typerepr.ExistentialTypeRepr -import codeql.swift.elements.typerepr.FixedTypeRepr -import codeql.swift.elements.typerepr.FunctionTypeRepr -import codeql.swift.elements.typerepr.GenericIdentTypeRepr -import codeql.swift.elements.typerepr.IdentTypeRepr -import codeql.swift.elements.typerepr.ImplicitlyUnwrappedOptionalTypeRepr -import codeql.swift.elements.typerepr.InOutTypeRepr -import codeql.swift.elements.typerepr.IsolatedTypeRepr -import codeql.swift.elements.typerepr.MetatypeTypeRepr -import codeql.swift.elements.typerepr.NamedOpaqueReturnTypeRepr -import codeql.swift.elements.typerepr.OpaqueReturnTypeRepr -import codeql.swift.elements.typerepr.OptionalTypeRepr -import codeql.swift.elements.typerepr.OwnedTypeRepr -import codeql.swift.elements.typerepr.PlaceholderTypeRepr -import codeql.swift.elements.typerepr.ProtocolTypeRepr -import codeql.swift.elements.typerepr.SharedTypeRepr -import codeql.swift.elements.typerepr.SilBoxTypeRepr -import codeql.swift.elements.typerepr.SimpleIdentTypeRepr -import codeql.swift.elements.typerepr.SpecifierTypeRepr -import codeql.swift.elements.typerepr.TupleTypeRepr -import codeql.swift.elements.typerepr.TypeRepr diff --git a/swift/ql/lib/codeql/swift/elements/Comment.qll b/swift/ql/lib/codeql/swift/elements/Comment.qll new file mode 100644 index 00000000000..c0fb2089e3b --- /dev/null +++ b/swift/ql/lib/codeql/swift/elements/Comment.qll @@ -0,0 +1,35 @@ +private import codeql.swift.generated.Comment + +class Comment extends CommentBase { + /** toString */ + override string toString() { result = getText() } +} + +class SingleLineComment extends Comment { + SingleLineComment() { + this.getText().matches("//%") and + not this instanceof SingleLineDocComment + } +} + +class MultiLineComment extends Comment { + MultiLineComment() { + this.getText().matches("/*%") and + not this instanceof MultiLineDocComment + } +} + +class DocComment extends Comment { + DocComment() { + this instanceof SingleLineDocComment or + this instanceof MultiLineDocComment + } +} + +class SingleLineDocComment extends Comment { + SingleLineDocComment() { this.getText().matches("///%") } +} + +class MultiLineDocComment extends Comment { + MultiLineDocComment() { this.getText().matches("/**%") } +} diff --git a/swift/ql/lib/codeql/swift/elements/decl/IfConfigClause.qll b/swift/ql/lib/codeql/swift/elements/decl/IfConfigClause.qll new file mode 100644 index 00000000000..6ab22e67989 --- /dev/null +++ b/swift/ql/lib/codeql/swift/elements/decl/IfConfigClause.qll @@ -0,0 +1,4 @@ +// generated by codegen/codegen.py, remove this comment if you wish to edit this file +private import codeql.swift.generated.decl.IfConfigClause + +class IfConfigClause extends IfConfigClauseBase { } diff --git a/swift/ql/lib/codeql/swift/elements/expr/Argument.qll b/swift/ql/lib/codeql/swift/elements/expr/Argument.qll index 88c11b14013..bec912414a6 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/Argument.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/Argument.qll @@ -5,4 +5,6 @@ class Argument extends ArgumentBase { override string toString() { result = this.getLabel() + ": " + this.getExpr().toString() } int getIndex() { any(ApplyExpr apply).getArgument(result) = this } + + ApplyExpr getApplyExpr() { result.getAnArgument() = this } } diff --git a/swift/ql/lib/codeql/swift/elements/expr/TapExpr.qll b/swift/ql/lib/codeql/swift/elements/expr/TapExpr.qll index e0608415f1d..e200b613823 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/TapExpr.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/TapExpr.qll @@ -1,4 +1,11 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file private import codeql.swift.generated.expr.TapExpr +/** + * A `TapExpr` is an internal expression generated by the Swift compiler. + * + * If `e` is a `TapExpr`, the semantics of evaluating `e` is: + * 1. Create a local variable `e.getVar()` and assign it the value `e.getSubExpr()`. + * 2. Execute `e.getBody()` which potentially modifies the local variable. + * 3. Return the value of the local variable. + */ class TapExpr extends TapExprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/expr/UnresolvedDeclRefExpr.qll b/swift/ql/lib/codeql/swift/elements/expr/UnresolvedDeclRefExpr.qll index 281e1db01a7..3cbe55fe9a6 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/UnresolvedDeclRefExpr.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/UnresolvedDeclRefExpr.qll @@ -1,4 +1,9 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file private import codeql.swift.generated.expr.UnresolvedDeclRefExpr -class UnresolvedDeclRefExpr extends UnresolvedDeclRefExprBase { } +class UnresolvedDeclRefExpr extends UnresolvedDeclRefExprBase { + override string toString() { + result = getName() + " (unresolved)" + or + not hasName() and result = "(unresolved)" + } +} diff --git a/swift/ql/lib/codeql/swift/elements/type/TypeRepr.qll b/swift/ql/lib/codeql/swift/elements/type/TypeRepr.qll new file mode 100644 index 00000000000..8462ceb1b6b --- /dev/null +++ b/swift/ql/lib/codeql/swift/elements/type/TypeRepr.qll @@ -0,0 +1,5 @@ +private import codeql.swift.generated.type.TypeRepr + +class TypeRepr extends TypeReprBase { + override string toString() { result = getType().toString() } +} diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/ComponentIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/ComponentIdentTypeRepr.qll deleted file mode 100644 index 0b5640208f4..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/ComponentIdentTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.ComponentIdentTypeRepr - -class ComponentIdentTypeRepr extends ComponentIdentTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/CompositionTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/CompositionTypeRepr.qll deleted file mode 100644 index 4f79f493e7e..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/CompositionTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.CompositionTypeRepr - -class CompositionTypeRepr extends CompositionTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/ErrorTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/ErrorTypeRepr.qll deleted file mode 100644 index bc3a125e0ac..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/ErrorTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.ErrorTypeRepr - -class ErrorTypeRepr extends ErrorTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/ExistentialTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/ExistentialTypeRepr.qll deleted file mode 100644 index 7762a02bb64..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/ExistentialTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.ExistentialTypeRepr - -class ExistentialTypeRepr extends ExistentialTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/FixedTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/FixedTypeRepr.qll deleted file mode 100644 index 8c1687c8853..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/FixedTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.FixedTypeRepr - -class FixedTypeRepr extends FixedTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/IdentTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/IdentTypeRepr.qll deleted file mode 100644 index 5fa70e354e4..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/IdentTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.IdentTypeRepr - -class IdentTypeRepr extends IdentTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/NamedOpaqueReturnTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/NamedOpaqueReturnTypeRepr.qll deleted file mode 100644 index 115b02b9858..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/NamedOpaqueReturnTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.NamedOpaqueReturnTypeRepr - -class NamedOpaqueReturnTypeRepr extends NamedOpaqueReturnTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/OpaqueReturnTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/OpaqueReturnTypeRepr.qll deleted file mode 100644 index cd0586d67f4..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/OpaqueReturnTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.OpaqueReturnTypeRepr - -class OpaqueReturnTypeRepr extends OpaqueReturnTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/PlaceholderTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/PlaceholderTypeRepr.qll deleted file mode 100644 index 423ced1a5d8..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/PlaceholderTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.PlaceholderTypeRepr - -class PlaceholderTypeRepr extends PlaceholderTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/SimpleIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/SimpleIdentTypeRepr.qll deleted file mode 100644 index 070146f5ace..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/SimpleIdentTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.SimpleIdentTypeRepr - -class SimpleIdentTypeRepr extends SimpleIdentTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/SpecifierTypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/SpecifierTypeRepr.qll deleted file mode 100644 index 40d1d648881..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/SpecifierTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.SpecifierTypeRepr - -class SpecifierTypeRepr extends SpecifierTypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/elements/typerepr/TypeRepr.qll b/swift/ql/lib/codeql/swift/elements/typerepr/TypeRepr.qll deleted file mode 100644 index 4a4ea87833e..00000000000 --- a/swift/ql/lib/codeql/swift/elements/typerepr/TypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file -private import codeql.swift.generated.typerepr.TypeRepr - -class TypeRepr extends TypeReprBase { } diff --git a/swift/ql/lib/codeql/swift/generated/Comment.qll b/swift/ql/lib/codeql/swift/generated/Comment.qll new file mode 100644 index 00000000000..ee6263126db --- /dev/null +++ b/swift/ql/lib/codeql/swift/generated/Comment.qll @@ -0,0 +1,8 @@ +// generated by codegen/codegen.py +import codeql.swift.elements.Locatable + +class CommentBase extends @comment, Locatable { + override string getAPrimaryQlClass() { result = "Comment" } + + string getText() { comments(this, result) } +} diff --git a/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll b/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll index aaf3dec16bd..5b8b979d16f 100644 --- a/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll +++ b/swift/ql/lib/codeql/swift/generated/GetImmediateParent.qll @@ -28,6 +28,12 @@ Element getAnImmediateChild(Element e) { or enum_element_decl_params(e, _, x) or + if_config_clause_conditions(e, x) + or + if_config_clause_elements(e, _, x) + or + if_config_decl_clauses(e, _, x) + or pattern_binding_decl_inits(e, _, x) or pattern_binding_decl_patterns(e, _, x) @@ -64,11 +70,9 @@ Element getAnImmediateChild(Element e) { or dynamic_type_exprs(e, x) or - enum_is_case_exprs(e, x, _, _) + enum_is_case_exprs(e, x, _) or - enum_is_case_exprs(e, _, x, _) - or - enum_is_case_exprs(e, _, _, x) + enum_is_case_exprs(e, _, x) or explicit_cast_exprs(e, x) or @@ -96,7 +100,7 @@ Element getAnImmediateChild(Element e) { or key_path_application_exprs(e, _, x) or - key_path_expr_parsed_roots(e, x) + key_path_expr_roots(e, x) or key_path_expr_parsed_paths(e, x) or @@ -144,6 +148,8 @@ Element getAnImmediateChild(Element e) { or unresolved_dot_exprs(e, x, _) or + unresolved_pattern_exprs(e, x) + or vararg_expansion_exprs(e, x) or binding_patterns(e, x) diff --git a/swift/ql/lib/codeql/swift/generated/decl/IfConfigClause.qll b/swift/ql/lib/codeql/swift/generated/decl/IfConfigClause.qll new file mode 100644 index 00000000000..89a1cb277ed --- /dev/null +++ b/swift/ql/lib/codeql/swift/generated/decl/IfConfigClause.qll @@ -0,0 +1,30 @@ +// generated by codegen/codegen.py +import codeql.swift.elements.AstNode +import codeql.swift.elements.expr.Expr +import codeql.swift.elements.Locatable + +class IfConfigClauseBase extends @if_config_clause, Locatable { + override string getAPrimaryQlClass() { result = "IfConfigClause" } + + Expr getCondition() { + exists(Expr x | + if_config_clause_conditions(this, x) and + result = x.resolve() + ) + } + + predicate hasCondition() { exists(getCondition()) } + + AstNode getElement(int index) { + exists(AstNode x | + if_config_clause_elements(this, index, x) and + result = x.resolve() + ) + } + + AstNode getAnElement() { result = getElement(_) } + + int getNumberOfElements() { result = count(getAnElement()) } + + predicate isActive() { if_config_clause_is_active(this) } +} diff --git a/swift/ql/lib/codeql/swift/generated/decl/IfConfigDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/IfConfigDecl.qll index 578051e9f8b..5d13959a227 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/IfConfigDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/IfConfigDecl.qll @@ -1,6 +1,18 @@ // generated by codegen/codegen.py import codeql.swift.elements.decl.Decl +import codeql.swift.elements.decl.IfConfigClause class IfConfigDeclBase extends @if_config_decl, Decl { override string getAPrimaryQlClass() { result = "IfConfigDecl" } + + IfConfigClause getClause(int index) { + exists(IfConfigClause x | + if_config_decl_clauses(this, index, x) and + result = x.resolve() + ) + } + + IfConfigClause getAClause() { result = getClause(_) } + + int getNumberOfClauses() { result = count(getAClause()) } } diff --git a/swift/ql/lib/codeql/swift/generated/decl/ImportDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/ImportDecl.qll index fdd89db170a..8f5638ad069 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/ImportDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/ImportDecl.qll @@ -1,6 +1,28 @@ // generated by codegen/codegen.py import codeql.swift.elements.decl.Decl +import codeql.swift.elements.decl.ModuleDecl +import codeql.swift.elements.decl.ValueDecl class ImportDeclBase extends @import_decl, Decl { override string getAPrimaryQlClass() { result = "ImportDecl" } + + predicate isExported() { import_decl_is_exported(this) } + + ModuleDecl getModule() { + exists(ModuleDecl x | + import_decls(this, x) and + result = x.resolve() + ) + } + + ValueDecl getDeclaration(int index) { + exists(ValueDecl x | + import_decl_declarations(this, index, x) and + result = x.resolve() + ) + } + + ValueDecl getADeclaration() { result = getDeclaration(_) } + + int getNumberOfDeclarations() { result = count(getADeclaration()) } } diff --git a/swift/ql/lib/codeql/swift/generated/decl/ModuleDecl.qll b/swift/ql/lib/codeql/swift/generated/decl/ModuleDecl.qll index aad5d00c0c6..3a1931b2d52 100644 --- a/swift/ql/lib/codeql/swift/generated/decl/ModuleDecl.qll +++ b/swift/ql/lib/codeql/swift/generated/decl/ModuleDecl.qll @@ -3,4 +3,8 @@ import codeql.swift.elements.decl.TypeDecl class ModuleDeclBase extends @module_decl, TypeDecl { override string getAPrimaryQlClass() { result = "ModuleDecl" } + + predicate isBuiltinModule() { module_decl_is_builtin_module(this) } + + predicate isSystemModule() { module_decl_is_system_module(this) } } diff --git a/swift/ql/lib/codeql/swift/generated/expr/Argument.qll b/swift/ql/lib/codeql/swift/generated/expr/Argument.qll index 9f16b90e3fe..7e8d6ea0ed3 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/Argument.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/Argument.qll @@ -1,8 +1,8 @@ // generated by codegen/codegen.py -import codeql.swift.elements.Element import codeql.swift.elements.expr.Expr +import codeql.swift.elements.Locatable -class ArgumentBase extends @argument, Element { +class ArgumentBase extends @argument, Locatable { override string getAPrimaryQlClass() { result = "Argument" } string getLabel() { arguments(this, result, _) } diff --git a/swift/ql/lib/codeql/swift/generated/expr/EnumIsCaseExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/EnumIsCaseExpr.qll index e02b97813a4..1dfdb7d0d2c 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/EnumIsCaseExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/EnumIsCaseExpr.qll @@ -1,28 +1,20 @@ // generated by codegen/codegen.py import codeql.swift.elements.decl.EnumElementDecl import codeql.swift.elements.expr.Expr -import codeql.swift.elements.typerepr.TypeRepr class EnumIsCaseExprBase extends @enum_is_case_expr, Expr { override string getAPrimaryQlClass() { result = "EnumIsCaseExpr" } Expr getSubExpr() { exists(Expr x | - enum_is_case_exprs(this, x, _, _) and - result = x.resolve() - ) - } - - TypeRepr getTypeRepr() { - exists(TypeRepr x | - enum_is_case_exprs(this, _, x, _) and + enum_is_case_exprs(this, x, _) and result = x.resolve() ) } EnumElementDecl getElement() { exists(EnumElementDecl x | - enum_is_case_exprs(this, _, _, x) and + enum_is_case_exprs(this, _, x) and result = x.resolve() ) } diff --git a/swift/ql/lib/codeql/swift/generated/expr/KeyPathExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/KeyPathExpr.qll index 7ac3d4614f0..faeeb4fde9b 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/KeyPathExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/KeyPathExpr.qll @@ -1,17 +1,18 @@ // generated by codegen/codegen.py import codeql.swift.elements.expr.Expr +import codeql.swift.elements.type.TypeRepr class KeyPathExprBase extends @key_path_expr, Expr { override string getAPrimaryQlClass() { result = "KeyPathExpr" } - Expr getParsedRoot() { - exists(Expr x | - key_path_expr_parsed_roots(this, x) and + TypeRepr getRoot() { + exists(TypeRepr x | + key_path_expr_roots(this, x) and result = x.resolve() ) } - predicate hasParsedRoot() { exists(getParsedRoot()) } + predicate hasRoot() { exists(getRoot()) } Expr getParsedPath() { exists(Expr x | diff --git a/swift/ql/lib/codeql/swift/generated/expr/TypeExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/TypeExpr.qll index 8b9e238b3ca..3c5816624d1 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/TypeExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/TypeExpr.qll @@ -1,6 +1,6 @@ // generated by codegen/codegen.py import codeql.swift.elements.expr.Expr -import codeql.swift.elements.typerepr.TypeRepr +import codeql.swift.elements.type.TypeRepr class TypeExprBase extends @type_expr, Expr { override string getAPrimaryQlClass() { result = "TypeExpr" } diff --git a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll index 8d03b2079aa..ae62f2df23b 100644 --- a/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll +++ b/swift/ql/lib/codeql/swift/generated/expr/UnresolvedPatternExpr.qll @@ -1,6 +1,14 @@ // generated by codegen/codegen.py import codeql.swift.elements.expr.Expr +import codeql.swift.elements.pattern.Pattern class UnresolvedPatternExprBase extends @unresolved_pattern_expr, Expr { override string getAPrimaryQlClass() { result = "UnresolvedPatternExpr" } + + Pattern getSubPattern() { + exists(Pattern x | + unresolved_pattern_exprs(this, x) and + result = x.resolve() + ) + } } diff --git a/swift/ql/lib/codeql/swift/generated/pattern/IsPattern.qll b/swift/ql/lib/codeql/swift/generated/pattern/IsPattern.qll index 8caf5493b36..b83c0af7d65 100644 --- a/swift/ql/lib/codeql/swift/generated/pattern/IsPattern.qll +++ b/swift/ql/lib/codeql/swift/generated/pattern/IsPattern.qll @@ -1,6 +1,6 @@ // generated by codegen/codegen.py import codeql.swift.elements.pattern.Pattern -import codeql.swift.elements.typerepr.TypeRepr +import codeql.swift.elements.type.TypeRepr class IsPatternBase extends @is_pattern, Pattern { override string getAPrimaryQlClass() { result = "IsPattern" } diff --git a/swift/ql/lib/codeql/swift/generated/pattern/TypedPattern.qll b/swift/ql/lib/codeql/swift/generated/pattern/TypedPattern.qll index 9a16c9cfe02..aebb2a6527c 100644 --- a/swift/ql/lib/codeql/swift/generated/pattern/TypedPattern.qll +++ b/swift/ql/lib/codeql/swift/generated/pattern/TypedPattern.qll @@ -1,6 +1,6 @@ // generated by codegen/codegen.py import codeql.swift.elements.pattern.Pattern -import codeql.swift.elements.typerepr.TypeRepr +import codeql.swift.elements.type.TypeRepr class TypedPatternBase extends @typed_pattern, Pattern { override string getAPrimaryQlClass() { result = "TypedPattern" } diff --git a/swift/ql/lib/codeql/swift/generated/type/ModuleType.qll b/swift/ql/lib/codeql/swift/generated/type/ModuleType.qll index 91da39854d7..866598ad431 100644 --- a/swift/ql/lib/codeql/swift/generated/type/ModuleType.qll +++ b/swift/ql/lib/codeql/swift/generated/type/ModuleType.qll @@ -1,6 +1,14 @@ // generated by codegen/codegen.py +import codeql.swift.elements.decl.ModuleDecl import codeql.swift.elements.type.Type class ModuleTypeBase extends @module_type, Type { override string getAPrimaryQlClass() { result = "ModuleType" } + + ModuleDecl getModule() { + exists(ModuleDecl x | + module_types(this, x) and + result = x.resolve() + ) + } } diff --git a/swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll b/swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll new file mode 100644 index 00000000000..5cc1cc04147 --- /dev/null +++ b/swift/ql/lib/codeql/swift/generated/type/TypeRepr.qll @@ -0,0 +1,16 @@ +// generated by codegen/codegen.py +import codeql.swift.elements.AstNode +import codeql.swift.elements.type.Type + +class TypeReprBase extends @type_repr, AstNode { + override string getAPrimaryQlClass() { result = "TypeRepr" } + + Type getType() { + exists(Type x | + type_repr_types(this, x) and + result = x.resolve() + ) + } + + predicate hasType() { exists(getType()) } +} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ArrayTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ArrayTypeRepr.qll deleted file mode 100644 index 486a98a541e..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ArrayTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class ArrayTypeReprBase extends @array_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "ArrayTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/AttributedTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/AttributedTypeRepr.qll deleted file mode 100644 index 96d78c7c933..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/AttributedTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class AttributedTypeReprBase extends @attributed_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "AttributedTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/CompileTimeConstTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/CompileTimeConstTypeRepr.qll deleted file mode 100644 index eecb67b9218..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/CompileTimeConstTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.SpecifierTypeRepr - -class CompileTimeConstTypeReprBase extends @compile_time_const_type_repr, SpecifierTypeRepr { - override string getAPrimaryQlClass() { result = "CompileTimeConstTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ComponentIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ComponentIdentTypeRepr.qll deleted file mode 100644 index d0323ad681d..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ComponentIdentTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.IdentTypeRepr - -class ComponentIdentTypeReprBase extends @component_ident_type_repr, IdentTypeRepr { } diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/CompositionTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/CompositionTypeRepr.qll deleted file mode 100644 index b0789100618..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/CompositionTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class CompositionTypeReprBase extends @composition_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "CompositionTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/CompoundIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/CompoundIdentTypeRepr.qll deleted file mode 100644 index 7c1c3548272..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/CompoundIdentTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.IdentTypeRepr - -class CompoundIdentTypeReprBase extends @compound_ident_type_repr, IdentTypeRepr { - override string getAPrimaryQlClass() { result = "CompoundIdentTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/DictionaryTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/DictionaryTypeRepr.qll deleted file mode 100644 index 692b9352e20..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/DictionaryTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class DictionaryTypeReprBase extends @dictionary_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "DictionaryTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ErrorTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ErrorTypeRepr.qll deleted file mode 100644 index 511f7e25569..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ErrorTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class ErrorTypeReprBase extends @error_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "ErrorTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ExistentialTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ExistentialTypeRepr.qll deleted file mode 100644 index 43aefeed5a9..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ExistentialTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class ExistentialTypeReprBase extends @existential_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "ExistentialTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/FixedTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/FixedTypeRepr.qll deleted file mode 100644 index 2a6e59a90a8..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/FixedTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class FixedTypeReprBase extends @fixed_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "FixedTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/FunctionTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/FunctionTypeRepr.qll deleted file mode 100644 index da781a90d4b..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/FunctionTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class FunctionTypeReprBase extends @function_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "FunctionTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/GenericIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/GenericIdentTypeRepr.qll deleted file mode 100644 index 101985aca56..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/GenericIdentTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.ComponentIdentTypeRepr - -class GenericIdentTypeReprBase extends @generic_ident_type_repr, ComponentIdentTypeRepr { - override string getAPrimaryQlClass() { result = "GenericIdentTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/IdentTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/IdentTypeRepr.qll deleted file mode 100644 index d71eca38b92..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/IdentTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class IdentTypeReprBase extends @ident_type_repr, TypeRepr { } diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr.qll deleted file mode 100644 index e0759e0fa3c..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr.qll +++ /dev/null @@ -1,7 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class ImplicitlyUnwrappedOptionalTypeReprBase extends @implicitly_unwrapped_optional_type_repr, - TypeRepr { - override string getAPrimaryQlClass() { result = "ImplicitlyUnwrappedOptionalTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/InOutTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/InOutTypeRepr.qll deleted file mode 100644 index a7adf859579..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/InOutTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.SpecifierTypeRepr - -class InOutTypeReprBase extends @in_out_type_repr, SpecifierTypeRepr { - override string getAPrimaryQlClass() { result = "InOutTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/IsolatedTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/IsolatedTypeRepr.qll deleted file mode 100644 index 63dcdd9e379..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/IsolatedTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.SpecifierTypeRepr - -class IsolatedTypeReprBase extends @isolated_type_repr, SpecifierTypeRepr { - override string getAPrimaryQlClass() { result = "IsolatedTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/MetatypeTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/MetatypeTypeRepr.qll deleted file mode 100644 index e0477db158b..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/MetatypeTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class MetatypeTypeReprBase extends @metatype_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "MetatypeTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/NamedOpaqueReturnTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/NamedOpaqueReturnTypeRepr.qll deleted file mode 100644 index af885d21ab4..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/NamedOpaqueReturnTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class NamedOpaqueReturnTypeReprBase extends @named_opaque_return_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "NamedOpaqueReturnTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/OpaqueReturnTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/OpaqueReturnTypeRepr.qll deleted file mode 100644 index 7ecb38e22c6..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/OpaqueReturnTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class OpaqueReturnTypeReprBase extends @opaque_return_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "OpaqueReturnTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/OptionalTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/OptionalTypeRepr.qll deleted file mode 100644 index 62a24285ef5..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/OptionalTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class OptionalTypeReprBase extends @optional_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "OptionalTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/OwnedTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/OwnedTypeRepr.qll deleted file mode 100644 index b24ca1aa597..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/OwnedTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.SpecifierTypeRepr - -class OwnedTypeReprBase extends @owned_type_repr, SpecifierTypeRepr { - override string getAPrimaryQlClass() { result = "OwnedTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/PlaceholderTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/PlaceholderTypeRepr.qll deleted file mode 100644 index 5bb446c9c73..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/PlaceholderTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class PlaceholderTypeReprBase extends @placeholder_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "PlaceholderTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/ProtocolTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/ProtocolTypeRepr.qll deleted file mode 100644 index 1b2e7fb2652..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/ProtocolTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class ProtocolTypeReprBase extends @protocol_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "ProtocolTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/SharedTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/SharedTypeRepr.qll deleted file mode 100644 index ca9254217c9..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/SharedTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.SpecifierTypeRepr - -class SharedTypeReprBase extends @shared_type_repr, SpecifierTypeRepr { - override string getAPrimaryQlClass() { result = "SharedTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/SilBoxTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/SilBoxTypeRepr.qll deleted file mode 100644 index 47d4585511b..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/SilBoxTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class SilBoxTypeReprBase extends @sil_box_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "SilBoxTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/SimpleIdentTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/SimpleIdentTypeRepr.qll deleted file mode 100644 index 328b8bc7e69..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/SimpleIdentTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.ComponentIdentTypeRepr - -class SimpleIdentTypeReprBase extends @simple_ident_type_repr, ComponentIdentTypeRepr { - override string getAPrimaryQlClass() { result = "SimpleIdentTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/SpecifierTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/SpecifierTypeRepr.qll deleted file mode 100644 index 469aa3413d1..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/SpecifierTypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class SpecifierTypeReprBase extends @specifier_type_repr, TypeRepr { } diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/TupleTypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/TupleTypeRepr.qll deleted file mode 100644 index 79e65037aa9..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/TupleTypeRepr.qll +++ /dev/null @@ -1,6 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.typerepr.TypeRepr - -class TupleTypeReprBase extends @tuple_type_repr, TypeRepr { - override string getAPrimaryQlClass() { result = "TupleTypeRepr" } -} diff --git a/swift/ql/lib/codeql/swift/generated/typerepr/TypeRepr.qll b/swift/ql/lib/codeql/swift/generated/typerepr/TypeRepr.qll deleted file mode 100644 index 497ac46fa64..00000000000 --- a/swift/ql/lib/codeql/swift/generated/typerepr/TypeRepr.qll +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements.AstNode - -class TypeReprBase extends @type_repr, AstNode { } diff --git a/swift/ql/lib/swift.dbscheme b/swift/ql/lib/swift.dbscheme index 7937e938e70..b520efbade5 100644 --- a/swift/ql/lib/swift.dbscheme +++ b/swift/ql/lib/swift.dbscheme @@ -13,8 +13,7 @@ sourceLocationPrefix( // from codegen/schema.yml @element = - @argument -| @callable + @callable | @file | @generic_context | @iterable_decl_context @@ -34,8 +33,11 @@ files( ); @locatable = - @ast_node + @argument +| @ast_node +| @comment | @condition_element +| @if_config_clause ; #keyset[id] @@ -53,6 +55,11 @@ locations( int end_column: int ref ); +comments( + unique int id: @comment, + string text: string ref +); + @type = @any_function_type | @any_generic_type @@ -263,7 +270,8 @@ l_value_types( //dir=type ); module_types( //dir=type - unique int id: @module_type + unique int id: @module_type, + int module: @module_decl ref ); placeholder_types( //dir=type @@ -471,27 +479,15 @@ expr_types( //dir=expr | @yield_stmt ; -@type_repr = - @array_type_repr -| @attributed_type_repr -| @composition_type_repr -| @dictionary_type_repr -| @error_type_repr -| @existential_type_repr -| @fixed_type_repr -| @function_type_repr -| @ident_type_repr -| @implicitly_unwrapped_optional_type_repr -| @metatype_type_repr -| @named_opaque_return_type_repr -| @opaque_return_type_repr -| @optional_type_repr -| @placeholder_type_repr -| @protocol_type_repr -| @sil_box_type_repr -| @specifier_type_repr -| @tuple_type_repr -; +type_reprs( //dir=type + unique int id: @type_repr +); + +#keyset[id] +type_repr_types( //dir=type + int id: @type_repr ref, + int type_: @type ref +); function_types( //dir=type unique int id: @function_type @@ -643,8 +639,50 @@ if_config_decls( //dir=decl unique int id: @if_config_decl ); +#keyset[id, index] +if_config_decl_clauses( //dir=decl + int id: @if_config_decl ref, + int index: int ref, + int clause: @if_config_clause ref +); + +if_config_clauses( //dir=decl + unique int id: @if_config_clause +); + +#keyset[id] +if_config_clause_conditions( //dir=decl + int id: @if_config_clause ref, + int condition: @expr ref +); + +#keyset[id, index] +if_config_clause_elements( //dir=decl + int id: @if_config_clause ref, + int index: int ref, + int element: @ast_node ref +); + +#keyset[id] +if_config_clause_is_active( //dir=decl + int id: @if_config_clause ref +); + import_decls( //dir=decl - unique int id: @import_decl + unique int id: @import_decl, + int module: @module_decl ref +); + +#keyset[id] +import_decl_is_exported( //dir=decl + int id: @import_decl ref +); + +#keyset[id, index] +import_decl_declarations( //dir=decl + int id: @import_decl ref, + int index: int ref, + int declaration: @value_decl ref ); missing_member_decls( //dir=decl @@ -852,7 +890,6 @@ editor_placeholder_exprs( //dir=expr enum_is_case_exprs( //dir=expr unique int id: @enum_is_case_expr, int sub_expr: @expr ref, - int type_repr: @type_repr ref, int element: @enum_element_decl ref ); @@ -956,9 +993,9 @@ key_path_exprs( //dir=expr ); #keyset[id] -key_path_expr_parsed_roots( //dir=expr +key_path_expr_roots( //dir=expr int id: @key_path_expr ref, - int parsed_root: @expr ref + int root: @type_repr ref ); #keyset[id] @@ -1128,7 +1165,8 @@ unresolved_member_exprs( //dir=expr ); unresolved_pattern_exprs( //dir=expr - unique int id: @unresolved_pattern_expr + unique int id: @unresolved_pattern_expr, + int sub_pattern: @pattern ref ); unresolved_specialize_exprs( //dir=expr @@ -2018,6 +2056,16 @@ module_decls( //dir=decl unique int id: @module_decl ); +#keyset[id] +module_decl_is_builtin_module( //dir=decl + int id: @module_decl ref +); + +#keyset[id] +module_decl_is_system_module( //dir=decl + int id: @module_decl ref +); + constructor_ref_call_exprs( //dir=expr unique int id: @constructor_ref_call_expr ); @@ -2154,121 +2202,3 @@ integer_literal_exprs( //dir=expr unique int id: @integer_literal_expr, string string_value: string ref ); - -error_type_reprs( //dir=typerepr - unique int id: @error_type_repr -); - -attributed_type_reprs( //dir=typerepr - unique int id: @attributed_type_repr -); - -@ident_type_repr = - @component_ident_type_repr -| @compound_ident_type_repr -; - -@component_ident_type_repr = - @generic_ident_type_repr -| @simple_ident_type_repr -; - -simple_ident_type_reprs( //dir=typerepr - unique int id: @simple_ident_type_repr -); - -generic_ident_type_reprs( //dir=typerepr - unique int id: @generic_ident_type_repr -); - -compound_ident_type_reprs( //dir=typerepr - unique int id: @compound_ident_type_repr -); - -function_type_reprs( //dir=typerepr - unique int id: @function_type_repr -); - -array_type_reprs( //dir=typerepr - unique int id: @array_type_repr -); - -dictionary_type_reprs( //dir=typerepr - unique int id: @dictionary_type_repr -); - -optional_type_reprs( //dir=typerepr - unique int id: @optional_type_repr -); - -implicitly_unwrapped_optional_type_reprs( //dir=typerepr - unique int id: @implicitly_unwrapped_optional_type_repr -); - -tuple_type_reprs( //dir=typerepr - unique int id: @tuple_type_repr -); - -composition_type_reprs( //dir=typerepr - unique int id: @composition_type_repr -); - -metatype_type_reprs( //dir=typerepr - unique int id: @metatype_type_repr -); - -protocol_type_reprs( //dir=typerepr - unique int id: @protocol_type_repr -); - -opaque_return_type_reprs( //dir=typerepr - unique int id: @opaque_return_type_repr -); - -named_opaque_return_type_reprs( //dir=typerepr - unique int id: @named_opaque_return_type_repr -); - -existential_type_reprs( //dir=typerepr - unique int id: @existential_type_repr -); - -placeholder_type_reprs( //dir=typerepr - unique int id: @placeholder_type_repr -); - -@specifier_type_repr = - @compile_time_const_type_repr -| @in_out_type_repr -| @isolated_type_repr -| @owned_type_repr -| @shared_type_repr -; - -in_out_type_reprs( //dir=typerepr - unique int id: @in_out_type_repr -); - -shared_type_reprs( //dir=typerepr - unique int id: @shared_type_repr -); - -owned_type_reprs( //dir=typerepr - unique int id: @owned_type_repr -); - -isolated_type_reprs( //dir=typerepr - unique int id: @isolated_type_repr -); - -compile_time_const_type_reprs( //dir=typerepr - unique int id: @compile_time_const_type_repr -); - -fixed_type_reprs( //dir=typerepr - unique int id: @fixed_type_repr -); - -sil_box_type_reprs( //dir=typerepr - unique int id: @sil_box_type_repr -); diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.qhelp b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.qhelp new file mode 100644 index 00000000000..1d61dbe9e92 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.qhelp @@ -0,0 +1,32 @@ + + + +

    Fetching data in a WebView without restricting the base URL may allow an attacker to access sensitive local data, for example using file://. Data can then be extracted from the software using the URL of a machine under the attackers control. More generally, an attacker may use a URL under their control as part of a cross-site scripting attack.

    + +
    + + +

    When loading HTML into a web view, always set the baseURL to an appropriate URL that you control, or to about:blank. Do not use nil, as this does not restrict URLs that can be resolved. Also do not use a baseURL that could itself be controlled by an attacker.

    + +
    + + +

    In the following example, a call to UIWebView.loadHTMLString has the baseURL set to nil, which does not restrict URLs that can be resolved from within the web page.

    + + + +

    To fix the problem, we set the baseURL to about:blank. This ensures that an attacker cannot resolve URLs that point to the local file system, or to web servers under their control.

    + + + +
    + + +
  • + iOS Bug Hunting - Web View XSS +
  • + +
    +
    \ No newline at end of file diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql new file mode 100644 index 00000000000..b952a0c74c3 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetch.ql @@ -0,0 +1,125 @@ +/** + * @name Unsafe WebView fetch + * @description Fetching data in a WebView without restricting the base URL may allow an attacker to access sensitive local data, or enable cross-site scripting attack. + * @kind path-problem + * @problem.severity warning + * @security-severity 6.1 + * @precision high + * @id swift/unsafe-webview-fetch + * @tags security + * external/cwe/cwe-079 + * external/cwe/cwe-095 + * external/cwe/cwe-749 + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources +import DataFlow::PathGraph + +/** + * A sink that is a candidate result for this query, such as certain arguments + * to `UIWebView.loadHTMLString`. + */ +class Sink extends DataFlow::Node { + Expr baseUrl; + + Sink() { + exists( + AbstractFunctionDecl funcDecl, CallExpr call, string funcName, string paramName, int arg, + int baseUrlArg + | + // arguments to method calls... + exists(string className, ClassDecl c | + ( + // `loadHTMLString` + className = ["UIWebView", "WKWebView"] and + funcName = "loadHTMLString(_:baseURL:)" and + paramName = "string" + or + // `UIWebView.load` + className = "UIWebView" and + funcName = "load(_:mimeType:textEncodingName:baseURL:)" and + paramName = "data" + or + // `WKWebView.load` + className = "WKWebView" and + funcName = "load(_:mimeType:characterEncodingName:baseURL:)" and + paramName = "data" + ) and + c.getName() = className and + c.getAMember() = funcDecl and + call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl + ) and + // match up `funcName`, `paramName`, `arg`, `node`. + funcDecl.getName() = funcName and + funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and + call.getArgument(pragma[only_bind_into](arg)).getExpr() = this.asExpr() and + // match up `baseURLArg` + funcDecl.getParam(pragma[only_bind_into](baseUrlArg)).getName() = "baseURL" and + call.getArgument(pragma[only_bind_into](baseUrlArg)).getExpr() = baseUrl + ) + } + + /** + * Gets the `baseURL` argument associated with this sink. + */ + Expr getBaseUrl() { result = baseUrl } +} + +/** + * A taint configuration from taint sources to sinks (and `baseURL` arguments) + * for this query. + */ +class UnsafeWebViewFetchConfig extends TaintTracking::Configuration { + UnsafeWebViewFetchConfig() { this = "UnsafeWebViewFetchConfig" } + + override predicate isSource(DataFlow::Node node) { node instanceof RemoteFlowSource } + + override predicate isSink(DataFlow::Node node) { + node instanceof Sink or + node.asExpr() = any(Sink s).getBaseUrl() + } + + override predicate isAdditionalTaintStep(DataFlow::Node node1, DataFlow::Node node2) { + // allow flow through `try!` and similar constructs + // TODO: this should probably be part of DataFlow / TaintTracking. + node1.asExpr() = node2.asExpr().(AnyTryExpr).getSubExpr() + or + // allow flow through `!` + // TODO: this should probably be part of DataFlow / TaintTracking. + node1.asExpr() = node2.asExpr().(ForceValueExpr).getSubExpr() + or + // allow flow through string concatenation. + // TODO: this should probably be part of TaintTracking. + node2.asExpr().(AddExpr).getAnOperand() = node1.asExpr() + or + // allow flow through `URL.init`. + exists(CallExpr call, ClassDecl c, AbstractFunctionDecl f | + c.getName() = "URL" and + c.getAMember() = f and + f.getName() = ["init(string:)", "init(string:relativeTo:)"] and + call.getFunction().(ApplyExpr).getStaticTarget() = f and + node1.asExpr() = call.getAnArgument().getExpr() and + node2.asExpr() = call + ) + } +} + +from + UnsafeWebViewFetchConfig config, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode, + Sink sink, string message +where + config.hasFlowPath(sourceNode, sinkNode) and + sink = sinkNode.getNode() and + ( + // base URL is nil + sink.getBaseUrl() instanceof NilLiteralExpr and + message = "Tainted data is used in a WebView fetch without restricting the base URL." + or + // base URL is tainted + config.hasFlowToExpr(sink.getBaseUrl()) and + message = "Tainted data is used in a WebView fetch with a tainted base URL." + ) +select sink, sourceNode, sinkNode, message diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetchBad.swift b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetchBad.swift new file mode 100644 index 00000000000..f85adb1b267 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetchBad.swift @@ -0,0 +1,6 @@ + +let webview = UIWebView() + +... + +webview.loadHTMLString(htmlData, baseURL: nil) // BAD diff --git a/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetchGood.swift b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetchGood.swift new file mode 100644 index 00000000000..5d89e5be672 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-079/UnsafeWebViewFetchGood.swift @@ -0,0 +1,6 @@ + +let webview = UIWebView() + +... + +webview.loadHTMLString(htmlData, baseURL: URL(string: "about:blank")) // GOOD diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.qhelp b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.qhelp index a13dbd80152..15e99e7e407 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.qhelp +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.qhelp @@ -5,6 +5,8 @@

    Using a length value from an NSString in a String, or a count from a String in an NSString, may cause unexpected behavior including (in some cases) buffer overwrites. This is because certain unicode sequences are represented as one character in a String but as a sequence of multiple characters in an NSString. For example, a 'thumbs up' emoji with a skin tone modifier (👍🏿) is represented as U+1F44D (👍) then the modifier U+1F3FF.

    +

    This issue can also arise from using the values of String.utf8.count, String.utf16.count or String.unicodeScalars.count in an unsuitable place.

    +
    diff --git a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql index a4e731e18ea..c9bdff012ce 100644 --- a/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql +++ b/swift/ql/src/queries/Security/CWE-135/StringLengthConflation.ql @@ -14,6 +14,39 @@ import swift import codeql.swift.dataflow.DataFlow import DataFlow::PathGraph +/** + * A flow state for this query, which is a type of Swift string encoding. + */ +class StringLengthConflationFlowState extends string { + string equivClass; + string singular; + + StringLengthConflationFlowState() { + this = "String" and singular = "a String" and equivClass = "String" + or + this = "NSString" and singular = "an NSString" and equivClass = "NSString" + or + this = "String.utf8" and singular = "a String.utf8" and equivClass = "String.utf8" + or + this = "String.utf16" and singular = "a String.utf16" and equivClass = "NSString" + or + this = "String.unicodeScalars" and + singular = "a String.unicodeScalars" and + equivClass = "String.unicodeScalars" + } + + /** + * Gets the equivalence class for this flow state. If these are equal, + * they should be treated as equivalent. + */ + string getEquivClass() { result = equivClass } + + /** + * Gets text for the singular form of this flow state. + */ + string getSingular() { result = singular } +} + /** * A configuration for tracking string lengths originating from source that is * a `String` or an `NSString` object, to a sink of a different kind that @@ -23,86 +56,154 @@ class StringLengthConflationConfiguration extends DataFlow::Configuration { StringLengthConflationConfiguration() { this = "StringLengthConflationConfiguration" } override predicate isSource(DataFlow::Node node, string flowstate) { - // result of a call to to `String.count` + // result of a call to `String.count` exists(MemberRefExpr member | member.getBaseExpr().getType().getName() = "String" and member.getMember().(VarDecl).getName() = "count" and node.asExpr() = member and flowstate = "String" ) + or + // result of a call to `NSString.length` + exists(MemberRefExpr member | + member.getBaseExpr().getType().getName() = ["NSString", "NSMutableString"] and + member.getMember().(VarDecl).getName() = "length" and + node.asExpr() = member and + flowstate = "NSString" + ) + or + // result of a call to `String.utf8.count` + exists(MemberRefExpr member | + member.getBaseExpr().getType().getName() = "String.UTF8View" and + member.getMember().(VarDecl).getName() = "count" and + node.asExpr() = member and + flowstate = "String.utf8" + ) + or + // result of a call to `String.utf16.count` + exists(MemberRefExpr member | + member.getBaseExpr().getType().getName() = "String.UTF16View" and + member.getMember().(VarDecl).getName() = "count" and + node.asExpr() = member and + flowstate = "String.utf16" + ) + or + // result of a call to `String.unicodeScalars.count` + exists(MemberRefExpr member | + member.getBaseExpr().getType().getName() = "String.UnicodeScalarView" and + member.getMember().(VarDecl).getName() = "count" and + node.asExpr() = member and + flowstate = "String.unicodeScalars" + ) + } + + /** + * Holds if `node` is a sink and `flowstate` is the *correct* flow state for + * that sink. We actually want to report incorrect flow states. + */ + predicate isSinkImpl(DataFlow::Node node, string flowstate) { + exists( + AbstractFunctionDecl funcDecl, CallExpr call, string funcName, string paramName, int arg + | + ( + // arguments to method calls... + exists(string className, ClassDecl c | + ( + // `NSRange.init` + className = "NSRange" and + funcName = "init(location:length:)" and + paramName = ["location", "length"] + or + // `NSString.character` + className = ["NSString", "NSMutableString"] and + funcName = "character(at:)" and + paramName = "at" + or + // `NSString.character` + className = ["NSString", "NSMutableString"] and + funcName = "substring(from:)" and + paramName = "from" + or + // `NSString.character` + className = ["NSString", "NSMutableString"] and + funcName = "substring(to:)" and + paramName = "to" + or + // `NSMutableString.insert` + className = "NSMutableString" and + funcName = "insert(_:at:)" and + paramName = "at" + ) and + c.getName() = className and + c.getAMember() = funcDecl and + call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl and + flowstate = "NSString" + ) + or + // arguments to function calls... + // `NSMakeRange` + funcName = "NSMakeRange(_:_:)" and + paramName = ["loc", "len"] and + call.getStaticTarget() = funcDecl and + flowstate = "NSString" + or + // arguments to method calls... + ( + // `String.dropFirst`, `String.dropLast`, `String.removeFirst`, `String.removeLast` + funcName = ["dropFirst(_:)", "dropLast(_:)", "removeFirst(_:)", "removeLast(_:)"] and + paramName = "k" + or + // `String.prefix`, `String.suffix` + funcName = ["prefix(_:)", "suffix(_:)"] and + paramName = "maxLength" + or + // `String.Index.init` + funcName = "init(encodedOffset:)" and + paramName = "offset" + or + // `String.index` + funcName = ["index(_:offsetBy:)", "index(_:offsetBy:limitBy:)"] and + paramName = "n" + or + // `String.formIndex` + funcName = ["formIndex(_:offsetBy:)", "formIndex(_:offsetBy:limitBy:)"] and + paramName = "distance" + ) and + call.getFunction().(ApplyExpr).getStaticTarget() = funcDecl and + flowstate = "String" + ) and + // match up `funcName`, `paramName`, `arg`, `node`. + funcDecl.getName() = funcName and + funcDecl.getParam(pragma[only_bind_into](arg)).getName() = paramName and + call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() + ) } override predicate isSink(DataFlow::Node node, string flowstate) { - // arguments to method calls... - exists( - string className, string methodName, string paramName, ClassDecl c, AbstractFunctionDecl f, - CallExpr call, int arg - | - ( - // `NSRange.init` - className = "NSRange" and - methodName = "init(location:length:)" and - paramName = ["location", "length"] - or - // `NSString.character` - className = ["NSString", "NSMutableString"] and - methodName = "character(at:)" and - paramName = "at" - or - // `NSString.character` - className = ["NSString", "NSMutableString"] and - methodName = "substring(from:)" and - paramName = "from" - or - // `NSString.character` - className = ["NSString", "NSMutableString"] and - methodName = "substring(to:)" and - paramName = "to" - or - // `NSMutableString.insert` - className = "NSMutableString" and - methodName = "insert(_:at:)" and - paramName = "at" - ) and - c.getName() = className and - c.getAMember() = f and // TODO: will this even work if its defined in a parent class? - call.getFunction().(ApplyExpr).getStaticTarget() = f and - f.getName() = methodName and - f.getParam(pragma[only_bind_into](arg)).getName() = paramName and - call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "String" // `String` length flowing into `NSString` - ) - or - // arguments to function calls... - exists(string funcName, string paramName, CallExpr call, int arg | - // `NSMakeRange` - funcName = "NSMakeRange(_:_:)" and - paramName = ["loc", "len"] and - call.getStaticTarget().getName() = funcName and - call.getStaticTarget().getParam(pragma[only_bind_into](arg)).getName() = paramName and - call.getArgument(pragma[only_bind_into](arg)).getExpr() = node.asExpr() and - flowstate = "String" // `String` length flowing into `NSString` + // Permit any *incorrect* flowstate, as those are the results the query + // should report. + exists(string correctFlowState | + isSinkImpl(node, correctFlowState) and + flowstate.(StringLengthConflationFlowState).getEquivClass() != + correctFlowState.(StringLengthConflationFlowState).getEquivClass() ) } override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { - // allow flow through `+` and `-`. - node2.asExpr().(AddExpr).getAnOperand() = node1.asExpr() or - node2.asExpr().(SubExpr).getAnOperand() = node1.asExpr() + // allow flow through `+`, `-`, `*` etc. + node2.asExpr().(ArithmeticOperation).getAnOperand() = node1.asExpr() } } from StringLengthConflationConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink, - string flowstate, string message + StringLengthConflationFlowState sourceFlowState, StringLengthConflationFlowState sinkFlowstate, + string message where config.hasFlowPath(source, sink) and - config.isSink(sink.getNode(), flowstate) and - ( - flowstate = "String" and - message = "This String length is used in an NSString, but it may not be equivalent." - or - flowstate = "NSString" and - message = "This NSString length is used in a String, but it may not be equivalent." - ) + config.isSource(source.getNode(), sourceFlowState) and + config.isSinkImpl(sink.getNode(), sinkFlowstate) and + message = + "This " + sourceFlowState + " length is used in " + sinkFlowstate.getSingular() + + ", but it may not be equivalent." select sink.getNode(), source, sink, message diff --git a/swift/ql/test/TestUtilities/InlineExpectationsTest.qll b/swift/ql/test/TestUtilities/InlineExpectationsTest.qll new file mode 100644 index 00000000000..4b4a31d6950 --- /dev/null +++ b/swift/ql/test/TestUtilities/InlineExpectationsTest.qll @@ -0,0 +1,377 @@ +/** + * Provides a library for writing QL tests whose success or failure is based on expected results + * embedded in the test source code as comments, rather than the contents of an `.expected` file + * (in that the `.expected` file should always be empty). + * + * To add this framework to a new language: + * - Add a file `InlineExpectationsTestPrivate.qll` that defines a `ExpectationComment` class. This class + * must support a `getContents` method that returns the contents of the given comment, _excluding_ + * the comment indicator itself. It should also define `toString` and `getLocation` as usual. + * + * To create a new inline expectations test: + * - Declare a class that extends `InlineExpectationsTest`. In the characteristic predicate of the + * new class, bind `this` to a unique string (usually the name of the test). + * - Override the `hasActualResult()` predicate to produce the actual results of the query. For each + * result, specify a `Location`, a text description of the element for which the result was + * reported, a short string to serve as the tag to identify expected results for this test, and the + * expected value of the result. + * - Override `getARelevantTag()` to return the set of tags that can be produced by + * `hasActualResult()`. Often this is just a single tag. + * + * Example: + * ```ql + * class ConstantValueTest extends InlineExpectationsTest { + * ConstantValueTest() { this = "ConstantValueTest" } + * + * override string getARelevantTag() { + * // We only use one tag for this test. + * result = "const" + * } + * + * override predicate hasActualResult( + * Location location, string element, string tag, string value + * ) { + * exists(Expr e | + * tag = "const" and // The tag for this test. + * value = e.getValue() and // The expected value. Will only hold for constant expressions. + * location = e.getLocation() and // The location of the result to be reported. + * element = e.toString() // The display text for the result. + * ) + * } + * } + * ``` + * + * There is no need to write a `select` clause or query predicate. All of the differences between + * expected results and actual results will be reported in the `failures()` query predicate. + * + * To annotate the test source code with an expected result, place a comment starting with a `$` on the + * same line as the expected result, with text of the following format as the body of the comment: + * + * `tag=expected-value` + * + * Where `tag` is the value of the `tag` parameter from `hasActualResult()`, and `expected-value` is + * the value of the `value` parameter from `hasActualResult()`. The `=expected-value` portion may be + * omitted, in which case `expected-value` is treated as the empty string. Multiple expectations may + * be placed in the same comment. Any actual result that + * appears on a line that does not contain a matching expected result comment will be reported with + * a message of the form "Unexpected result: tag=value". Any expected result comment for which there + * is no matching actual result will be reported with a message of the form + * "Missing result: tag=expected-value". + * + * Example: + * ```cpp + * int i = x + 5; // $ const=5 + * int j = y + (7 - 3) // $ const=7 const=3 const=4 // The result of the subtraction is a constant. + * ``` + * + * For tests that contain known missing and spurious results, it is possible to further + * annotate that a particular expected result is known to be spurious, or that a particular + * missing result is known to be missing: + * + * `$ SPURIOUS: tag=expected-value` // Spurious result + * `$ MISSING: tag=expected-value` // Missing result + * + * A spurious expectation is treated as any other expected result, except that if there is no + * matching actual result, the message will be of the form "Fixed spurious result: tag=value". A + * missing expectation is treated as if there were no expected result, except that if a + * matching expected result is found, the message will be of the form + * "Fixed missing result: tag=value". + * + * A single line can contain all the expected, spurious and missing results of that line. For instance: + * `$ tag1=value1 SPURIOUS: tag2=value2 MISSING: tag3=value3`. + * + * If the same result value is expected for two or more tags on the same line, there is a shorthand + * notation available: + * + * `tag1,tag2=expected-value` + * + * is equivalent to: + * + * `tag1=expected-value tag2=expected-value` + */ + +private import InlineExpectationsTestPrivate + +/** + * The base class for tests with inline expectations. The test extends this class to provide the actual + * results of the query, which are then compared with the expected results in comments to produce a + * list of failure messages that point out where the actual results differ from the expected + * results. + */ +abstract class InlineExpectationsTest extends string { + bindingset[this] + InlineExpectationsTest() { any() } + + /** + * Returns all tags that can be generated by this test. Most tests will only ever produce a single + * tag. Any expected result comments for a tag that is not returned by the `getARelevantTag()` + * predicate for an active test will be ignored. This makes it possible to write multiple tests in + * different `.ql` files that all query the same source code. + */ + abstract string getARelevantTag(); + + /** + * Returns the actual results of the query that is being tested. Each result consist of the + * following values: + * - `location` - The source code location of the result. Any expected result comment must appear + * on the start line of this location. + * - `element` - Display text for the element on which the result is reported. + * - `tag` - The tag that marks this result as coming from this test. This must be one of the tags + * returned by `getARelevantTag()`. + * - `value` - The value of the result, which will be matched against the value associated with + * `tag` in any expected result comment on that line. + */ + abstract predicate hasActualResult(Location location, string element, string tag, string value); + + /** + * Holds if there is an optional result on the specified location. + * + * This is similar to `hasActualResult`, but returns results that do not require a matching annotation. + * A failure will still arise if there is an annotation that does not match any results, but not vice versa. + * Override this predicate to specify optional results. + */ + predicate hasOptionalResult(Location location, string element, string tag, string value) { + none() + } + + final predicate hasFailureMessage(FailureLocatable element, string message) { + exists(ActualResult actualResult | + actualResult.getTest() = this and + element = actualResult and + ( + exists(FalseNegativeExpectation falseNegative | + falseNegative.matchesActualResult(actualResult) and + message = "Fixed missing result:" + falseNegative.getExpectationText() + ) + or + not exists(ValidExpectation expectation | expectation.matchesActualResult(actualResult)) and + message = "Unexpected result: " + actualResult.getExpectationText() and + not actualResult.isOptional() + ) + ) + or + exists(ValidExpectation expectation | + not exists(ActualResult actualResult | expectation.matchesActualResult(actualResult)) and + expectation.getTag() = getARelevantTag() and + element = expectation and + ( + expectation instanceof GoodExpectation and + message = "Missing result:" + expectation.getExpectationText() + or + expectation instanceof FalsePositiveExpectation and + message = "Fixed spurious result:" + expectation.getExpectationText() + ) + ) + or + exists(InvalidExpectation expectation | + element = expectation and + message = "Invalid expectation syntax: " + expectation.getExpectation() + ) + } +} + +/** + * RegEx pattern to match a comment containing one or more expected results. The comment must have + * `$` as its first non-whitespace character. Any subsequent character + * is treated as part of the expected results, except that the comment may contain a `//` sequence + * to treat the remainder of the line as a regular (non-interpreted) comment. + */ +private string expectationCommentPattern() { result = "\\s*\\$((?:[^/]|/[^/])*)(?://.*)?" } + +/** + * The possible columns in an expectation comment. The `TDefaultColumn` branch represents the first + * column in a comment. This column is not precedeeded by a name. `TNamedColumn(name)` represents a + * column containing expected results preceded by the string `name:`. + */ +private newtype TColumn = + TDefaultColumn() or + TNamedColumn(string name) { name = ["MISSING", "SPURIOUS"] } + +bindingset[start, content] +private int getEndOfColumnPosition(int start, string content) { + result = + min(string name, int cand | + exists(TNamedColumn(name)) and + cand = content.indexOf(name + ":") and + cand >= start + | + cand + ) + or + not exists(string name | + exists(TNamedColumn(name)) and + content.indexOf(name + ":") >= start + ) and + result = content.length() +} + +private predicate getAnExpectation( + ExpectationComment comment, TColumn column, string expectation, string tags, string value +) { + exists(string content | + content = comment.getContents().regexpCapture(expectationCommentPattern(), 1) and + ( + column = TDefaultColumn() and + exists(int end | + end = getEndOfColumnPosition(0, content) and + expectation = content.prefix(end).regexpFind(expectationPattern(), _, _).trim() + ) + or + exists(string name, int start, int end | + column = TNamedColumn(name) and + start = content.indexOf(name + ":") + name.length() + 1 and + end = getEndOfColumnPosition(start, content) and + expectation = content.substring(start, end).regexpFind(expectationPattern(), _, _).trim() + ) + ) + ) and + tags = expectation.regexpCapture(expectationPattern(), 1) and + if exists(expectation.regexpCapture(expectationPattern(), 2)) + then value = expectation.regexpCapture(expectationPattern(), 2) + else value = "" +} + +private string getColumnString(TColumn column) { + column = TDefaultColumn() and result = "" + or + column = TNamedColumn(result) +} + +/** + * RegEx pattern to match a single expected result, not including the leading `$`. It consists of one or + * 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 + tags = "((?:" + tag + ")(?:\\s*,\\s*" + tag + ")*)" and + // In Python, we allow both `"` and `'` for strings, as well as the prefixes `bru`. + // For example, `b"foo"`. + value = "((?:[bru]*\"[^\"]*\"|[bru]*'[^']*'|\\S+)*)" and + result = tags + "(?:=" + value + ")?" + ) +} + +private newtype TFailureLocatable = + TActualResult( + InlineExpectationsTest test, Location location, string element, string tag, string value, + boolean optional + ) { + test.hasActualResult(location, element, tag, value) and + optional = false + or + test.hasOptionalResult(location, element, tag, value) and optional = true + } or + TValidExpectation(ExpectationComment comment, string tag, string value, string knownFailure) { + exists(TColumn column, string tags | + getAnExpectation(comment, column, _, tags, value) and + tag = tags.splitAt(",") and + knownFailure = getColumnString(column) + ) + } or + TInvalidExpectation(ExpectationComment comment, string expectation) { + getAnExpectation(comment, _, expectation, _, _) and + not expectation.regexpMatch(expectationPattern()) + } + +class FailureLocatable extends TFailureLocatable { + string toString() { none() } + + Location getLocation() { none() } + + final string getExpectationText() { result = getTag() + "=" + getValue() } + + string getTag() { none() } + + string getValue() { none() } +} + +class ActualResult extends FailureLocatable, TActualResult { + InlineExpectationsTest test; + Location location; + string element; + string tag; + string value; + boolean optional; + + ActualResult() { this = TActualResult(test, location, element, tag, value, optional) } + + override string toString() { result = element } + + override Location getLocation() { result = location } + + InlineExpectationsTest getTest() { result = test } + + override string getTag() { result = tag } + + override string getValue() { result = value } + + predicate isOptional() { optional = true } +} + +abstract private class Expectation extends FailureLocatable { + ExpectationComment comment; + + override string toString() { result = comment.toString() } + + override Location getLocation() { result = comment.getLocation() } +} + +private class ValidExpectation extends Expectation, TValidExpectation { + string tag; + string value; + string knownFailure; + + ValidExpectation() { this = TValidExpectation(comment, tag, value, knownFailure) } + + override string getTag() { result = tag } + + override string getValue() { result = value } + + string getKnownFailure() { result = knownFailure } + + predicate matchesActualResult(ActualResult actualResult) { + getLocation().getStartLine() = actualResult.getLocation().getStartLine() and + getLocation().getFile() = actualResult.getLocation().getFile() and + getTag() = actualResult.getTag() and + getValue() = actualResult.getValue() + } +} + +/* Note: These next three classes correspond to all the possible values of type `TColumn`. */ +class GoodExpectation extends ValidExpectation { + GoodExpectation() { getKnownFailure() = "" } +} + +class FalsePositiveExpectation extends ValidExpectation { + FalsePositiveExpectation() { getKnownFailure() = "SPURIOUS" } +} + +class FalseNegativeExpectation extends ValidExpectation { + FalseNegativeExpectation() { getKnownFailure() = "MISSING" } +} + +class InvalidExpectation extends Expectation, TInvalidExpectation { + string expectation; + + InvalidExpectation() { this = TInvalidExpectation(comment, expectation) } + + string getExpectation() { result = expectation } +} + +query predicate failures(FailureLocatable element, string message) { + exists(InlineExpectationsTest test | test.hasFailureMessage(element, message)) +} diff --git a/swift/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll b/swift/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll new file mode 100644 index 00000000000..1776f04aea4 --- /dev/null +++ b/swift/ql/test/TestUtilities/InlineExpectationsTestPrivate.qll @@ -0,0 +1,23 @@ +import swift + +private newtype TExpectationComment = MkExpectationComment(SingleLineComment c) + +/** + * Represents a line comment. + * Unlike the `SingleLineComment` class, however, the string returned by `getContents` does _not_ + * include the preceding comment marker (`//`). + */ +class ExpectationComment extends TExpectationComment { + SingleLineComment comment; + + ExpectationComment() { this = MkExpectationComment(comment) } + + /** Returns the contents of the given comment, _without_ the preceding comment marker (`//`). */ + string getContents() { result = comment.getText().suffix(2) } + + /** Gets a textual representation of this element. */ + string toString() { result = comment.toString() } + + /** Gets the location of this comment. */ + Location getLocation() { result = comment.getLocation() } +} diff --git a/swift/ql/test/TestUtils.qll b/swift/ql/test/TestUtils.qll index 9359944fe90..265513b4f4a 100644 --- a/swift/ql/test/TestUtils.qll +++ b/swift/ql/test/TestUtils.qll @@ -4,6 +4,12 @@ cached predicate toBeTested(Element e) { e instanceof File or + exists(ModuleDecl m | + not m.isBuiltinModule() and + not m.isSystemModule() and + (m = e or m.getInterfaceType() = e) + ) + or exists(Locatable loc | loc.getLocation().getFile().getName().matches("%swift/ql/test%") and ( diff --git a/swift/ql/test/extractor-tests/comments/comments.expected b/swift/ql/test/extractor-tests/comments/comments.expected new file mode 100644 index 00000000000..d6e262e2f60 --- /dev/null +++ b/swift/ql/test/extractor-tests/comments/comments.expected @@ -0,0 +1,4 @@ +| comments.swift:1:1:2:1 | // Single line comment\n | +| comments.swift:3:1:6:3 | /*\n Multiline\n comment\n*/ | +| comments.swift:8:1:9:1 | /// Single line doc comment\n | +| comments.swift:10:1:13:3 | /**\n Multiline\n doc comment\n*/ | diff --git a/swift/ql/test/extractor-tests/comments/comments.ql b/swift/ql/test/extractor-tests/comments/comments.ql new file mode 100644 index 00000000000..53e23810346 --- /dev/null +++ b/swift/ql/test/extractor-tests/comments/comments.ql @@ -0,0 +1,4 @@ +import swift + +from Comment c +select c diff --git a/swift/ql/test/extractor-tests/comments/comments.swift b/swift/ql/test/extractor-tests/comments/comments.swift new file mode 100644 index 00000000000..1be189ec459 --- /dev/null +++ b/swift/ql/test/extractor-tests/comments/comments.swift @@ -0,0 +1,13 @@ +// Single line comment + +/* + Multiline + comment +*/ + +/// Single line doc comment + +/** + Multiline + doc comment +*/ diff --git a/swift/ql/test/extractor-tests/expressions/all.expected b/swift/ql/test/extractor-tests/expressions/all.expected index 20951358f73..b34ca7349cd 100644 --- a/swift/ql/test/extractor-tests/expressions/all.expected +++ b/swift/ql/test/extractor-tests/expressions/all.expected @@ -123,8 +123,6 @@ | expressions.swift:54:1:54:1 | _ | DiscardAssignmentExpr | | expressions.swift:54:1:54:8 | ... = ... | AssignExpr | | expressions.swift:54:5:54:8 | #keyPath(...) | KeyPathExpr | -| expressions.swift:54:6:54:6 | (no string representation) | TypeExpr | -| expressions.swift:54:6:54:8 | ... .x | UnresolvedDotExpr | | expressions.swift:58:16:58:16 | 1234 | IntegerLiteralExpr | | expressions.swift:59:1:59:1 | unsafeFunction(pointer:) | DeclRefExpr | | expressions.swift:59:1:59:34 | call to unsafeFunction(pointer:) | CallExpr | @@ -242,5 +240,3 @@ | expressions.swift:154:22:154:56 | \\...[...] | KeyPathApplicationExpr | | expressions.swift:154:33:154:33 | keyPathB | DeclRefExpr | | expressions.swift:154:52:154:55 | #keyPath(...) | KeyPathExpr | -| expressions.swift:154:53:154:53 | (no string representation) | TypeExpr | -| expressions.swift:154:53:154:55 | ... .x | UnresolvedDotExpr | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/Comment/MISSING_SOURCE.txt similarity index 100% rename from swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/MISSING_SOURCE.txt rename to swift/ql/test/extractor-tests/generated/Comment/MISSING_SOURCE.txt diff --git a/swift/ql/test/extractor-tests/generated/File/File.expected b/swift/ql/test/extractor-tests/generated/File/File.expected index d2ef49d491d..73437ea7a92 100644 --- a/swift/ql/test/extractor-tests/generated/File/File.expected +++ b/swift/ql/test/extractor-tests/generated/File/File.expected @@ -1,2 +1,3 @@ +| empty.swift:0:0:0:0 | empty.swift | getName: | empty.swift | | file://:0:0:0:0 | | getName: | | -| hello.swift:0:0:0:0 | hello.swift | getName: | hello.swift | +| non_empty.swift:0:0:0:0 | non_empty.swift | getName: | non_empty.swift | diff --git a/swift/ql/test/extractor-tests/generated/File/empty.swift b/swift/ql/test/extractor-tests/generated/File/empty.swift new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/File/non_empty.swift b/swift/ql/test/extractor-tests/generated/File/non_empty.swift new file mode 100644 index 00000000000..11b15b1a458 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/File/non_empty.swift @@ -0,0 +1 @@ +print("hello") diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected new file mode 100644 index 00000000000..f50475445e5 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.expected @@ -0,0 +1,7 @@ +| var_decls.swift:4:7:4:7 | i | getInterfaceType: | Int | getName: | i | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:7:5:7:5 | numbers | getInterfaceType: | [Int] | getName: | numbers | getType: | [Int] | getIntroducerInt: | 1 | +| var_decls.swift:10:12:10:12 | numbers | getInterfaceType: | [Int] | getName: | numbers | getType: | [Int] | getIntroducerInt: | 0 | +| var_decls.swift:15:7:15:7 | wrappedValue | getInterfaceType: | T | getName: | wrappedValue | getType: | T | getIntroducerInt: | 1 | +| var_decls.swift:20:7:20:7 | wrappedValue | getInterfaceType: | Int | getName: | wrappedValue | getType: | Int | getIntroducerInt: | 1 | +| var_decls.swift:24:15:24:15 | _wrapped | getInterfaceType: | X | getName: | _wrapped | getType: | X | getIntroducerInt: | 1 | +| var_decls.swift:24:15:24:15 | wrapped | getInterfaceType: | Int | getName: | wrapped | getType: | Int | getIntroducerInt: | 1 | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.ql new file mode 100644 index 00000000000..55ae04af6ac --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl.ql @@ -0,0 +1,14 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x, Type getInterfaceType, string getName, Type getType, int getIntroducerInt +where + toBeTested(x) and + not x.isUnknown() and + getInterfaceType = x.getInterfaceType() and + getName = x.getName() and + getType = x.getType() and + getIntroducerInt = x.getIntroducerInt() +select x, "getInterfaceType:", getInterfaceType, "getName:", getName, "getType:", getType, + "getIntroducerInt:", getIntroducerInt diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected new file mode 100644 index 00000000000..cfbce6fe5ef --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.expected @@ -0,0 +1,13 @@ +| var_decls.swift:10:12:10:12 | numbers | 0 | var_decls.swift:10:12:10:12 | get | +| var_decls.swift:15:7:15:7 | wrappedValue | 0 | var_decls.swift:15:7:15:7 | get | +| var_decls.swift:15:7:15:7 | wrappedValue | 1 | var_decls.swift:15:7:15:7 | set | +| var_decls.swift:15:7:15:7 | wrappedValue | 2 | var_decls.swift:15:7:15:7 | (unnamed function decl) | +| var_decls.swift:20:7:20:7 | wrappedValue | 0 | var_decls.swift:20:7:20:7 | get | +| var_decls.swift:20:7:20:7 | wrappedValue | 1 | var_decls.swift:20:7:20:7 | set | +| var_decls.swift:20:7:20:7 | wrappedValue | 2 | var_decls.swift:20:7:20:7 | (unnamed function decl) | +| var_decls.swift:24:15:24:15 | _wrapped | 0 | var_decls.swift:24:15:24:15 | get | +| var_decls.swift:24:15:24:15 | _wrapped | 1 | var_decls.swift:24:15:24:15 | set | +| var_decls.swift:24:15:24:15 | _wrapped | 2 | var_decls.swift:24:15:24:15 | (unnamed function decl) | +| var_decls.swift:24:15:24:15 | wrapped | 0 | var_decls.swift:24:15:24:15 | get | +| var_decls.swift:24:15:24:15 | wrapped | 1 | var_decls.swift:24:15:24:15 | set | +| var_decls.swift:24:15:24:15 | wrapped | 2 | var_decls.swift:24:15:24:15 | (unnamed function decl) | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.ql new file mode 100644 index 00000000000..b25caf7bf97 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAccessorDecl.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getAccessorDecl(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected new file mode 100644 index 00000000000..67bff5661b0 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.expected @@ -0,0 +1 @@ +| var_decls.swift:24:15:24:15 | wrapped | X | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.ql new file mode 100644 index 00000000000..569c25f0602 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getAttachedPropertyWrapperType.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getAttachedPropertyWrapperType() diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected new file mode 100644 index 00000000000..786b16fcab1 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.expected @@ -0,0 +1,3 @@ +| var_decls.swift:4:7:4:7 | i | var_decls.swift:4:11:4:11 | 0 | +| var_decls.swift:7:5:7:5 | numbers | var_decls.swift:7:15:7:18 | [...] | +| var_decls.swift:10:12:10:12 | numbers | var_decls.swift:10:22:10:35 | [...] | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.ql new file mode 100644 index 00000000000..eddfa346732 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentInitializer.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getParentInitializer() diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected new file mode 100644 index 00000000000..8e15539394a --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.expected @@ -0,0 +1,7 @@ +| var_decls.swift:4:7:4:7 | i | var_decls.swift:4:7:4:7 | i | +| var_decls.swift:7:5:7:5 | numbers | var_decls.swift:7:5:7:5 | numbers | +| var_decls.swift:10:12:10:12 | numbers | var_decls.swift:10:12:10:12 | numbers | +| var_decls.swift:15:7:15:7 | wrappedValue | var_decls.swift:15:7:15:21 | ... as ... | +| var_decls.swift:20:7:20:7 | wrappedValue | var_decls.swift:20:7:20:21 | ... as ... | +| var_decls.swift:24:15:24:15 | _wrapped | var_decls.swift:24:15:24:15 | ... as ... | +| var_decls.swift:24:15:24:15 | wrapped | var_decls.swift:24:15:24:25 | ... as ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.ql b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.ql new file mode 100644 index 00000000000..2aedb61e2ae --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/ConcreteVarDecl_getParentPattern.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ConcreteVarDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getParentPattern() diff --git a/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift new file mode 100644 index 00000000000..37c145a5a42 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ConcreteVarDecl/var_decls.swift @@ -0,0 +1,25 @@ +func loop() { + for i in 1...5 { + } + var i = 0 +} + +var numbers = [42] + +struct S { +static let numbers = [42, 404, 101] +} + +@propertyWrapper +struct X { + var wrappedValue: T +} + +@propertyWrapper +struct Y { + var wrappedValue: Int +} + +struct Wrapped { + @X @Y var wrapped : Int +} diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.expected new file mode 100644 index 00000000000..d4230ce830c --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.expected @@ -0,0 +1,6 @@ +| if_config.swift:1:1:1:1 | IfConfigClause | isActive: | no | +| if_config.swift:4:1:4:1 | IfConfigClause | isActive: | no | +| if_config.swift:7:1:7:1 | IfConfigClause | isActive: | yes | +| if_config_active.swift:3:1:3:1 | IfConfigClause | isActive: | yes | +| if_config_active.swift:6:1:6:1 | IfConfigClause | isActive: | no | +| if_config_active.swift:9:1:9:1 | IfConfigClause | isActive: | no | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.ql b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.ql new file mode 100644 index 00000000000..832025712f0 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause.ql @@ -0,0 +1,10 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from IfConfigClause x, string isActive +where + toBeTested(x) and + not x.isUnknown() and + if x.isActive() then isActive = "yes" else isActive = "no" +select x, "isActive:", isActive diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.expected new file mode 100644 index 00000000000..f4955665acd --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.expected @@ -0,0 +1,4 @@ +| if_config.swift:1:1:1:1 | IfConfigClause | if_config.swift:1:5:1:5 | FOO (unresolved) | +| if_config.swift:4:1:4:1 | IfConfigClause | if_config.swift:4:9:4:19 | call to ... | +| if_config_active.swift:3:1:3:1 | IfConfigClause | if_config_active.swift:3:5:3:5 | FOO (unresolved) | +| if_config_active.swift:6:1:6:1 | IfConfigClause | if_config_active.swift:6:9:6:17 | call to ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.ql b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.ql new file mode 100644 index 00000000000..174c1a4994d --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getCondition.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from IfConfigClause x +where toBeTested(x) and not x.isUnknown() +select x, x.getCondition() diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.expected new file mode 100644 index 00000000000..11dd2b54127 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.expected @@ -0,0 +1,18 @@ +| if_config.swift:1:1:1:1 | IfConfigClause | 0 | if_config.swift:2:1:2:16 | { ... } | +| if_config.swift:1:1:1:1 | IfConfigClause | 1 | if_config.swift:2:5:2:5 | foo | +| if_config.swift:1:1:1:1 | IfConfigClause | 2 | if_config.swift:3:1:3:12 | { ... } | +| if_config.swift:4:1:4:1 | IfConfigClause | 0 | if_config.swift:5:1:5:16 | { ... } | +| if_config.swift:4:1:4:1 | IfConfigClause | 1 | if_config.swift:5:5:5:5 | bar | +| if_config.swift:4:1:4:1 | IfConfigClause | 2 | if_config.swift:6:1:6:12 | { ... } | +| if_config.swift:7:1:7:1 | IfConfigClause | 0 | if_config.swift:8:1:8:16 | { ... } | +| if_config.swift:7:1:7:1 | IfConfigClause | 1 | if_config.swift:8:5:8:5 | baz | +| if_config.swift:7:1:7:1 | IfConfigClause | 2 | if_config.swift:9:1:9:12 | { ... } | +| if_config_active.swift:3:1:3:1 | IfConfigClause | 0 | if_config_active.swift:4:1:4:16 | { ... } | +| if_config_active.swift:3:1:3:1 | IfConfigClause | 1 | if_config_active.swift:4:5:4:5 | foo | +| if_config_active.swift:3:1:3:1 | IfConfigClause | 2 | if_config_active.swift:5:1:5:12 | { ... } | +| if_config_active.swift:6:1:6:1 | IfConfigClause | 0 | if_config_active.swift:7:1:7:16 | { ... } | +| if_config_active.swift:6:1:6:1 | IfConfigClause | 1 | if_config_active.swift:7:5:7:5 | bar | +| if_config_active.swift:6:1:6:1 | IfConfigClause | 2 | if_config_active.swift:8:1:8:12 | { ... } | +| if_config_active.swift:9:1:9:1 | IfConfigClause | 0 | if_config_active.swift:10:1:10:16 | { ... } | +| if_config_active.swift:9:1:9:1 | IfConfigClause | 1 | if_config_active.swift:10:5:10:5 | baz | +| if_config_active.swift:9:1:9:1 | IfConfigClause | 2 | if_config_active.swift:11:1:11:12 | { ... } | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.ql b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.ql new file mode 100644 index 00000000000..02c0eec17c9 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/IfConfigClause_getElement.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from IfConfigClause x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getElement(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config.swift b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config.swift new file mode 100644 index 00000000000..87cbc8ecd42 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config.swift @@ -0,0 +1,10 @@ +#if FOO +var foo: Int = 1 +print("foo") +#elseif os(watchOS) +var bar: Int = 2 +print("bar") +#else +var baz: Int = 3 +print("baz") +#endif diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config_active.swift b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config_active.swift new file mode 100644 index 00000000000..89849731bfe --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigClause/if_config_active.swift @@ -0,0 +1,12 @@ +//codeql-extractor-options: -D FOO + +#if FOO +var foo: Int = 1 +print("foo") +#elseif os(macOS) +var bar: Int = 2 +print("bar") +#else +var baz: Int = 3 +print("baz") +#endif diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.expected new file mode 100644 index 00000000000..3fed7a7fcc1 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.expected @@ -0,0 +1 @@ +| if_config.swift:1:1:10:1 | #if ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.ql b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.ql new file mode 100644 index 00000000000..a43d57b1d93 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from IfConfigDecl x +where toBeTested(x) and not x.isUnknown() +select x diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected new file mode 100644 index 00000000000..d90ba458397 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.expected @@ -0,0 +1,3 @@ +| if_config.swift:1:1:10:1 | #if ... | 0 | if_config.swift:1:1:1:1 | IfConfigClause | +| if_config.swift:1:1:10:1 | #if ... | 1 | if_config.swift:4:1:4:1 | IfConfigClause | +| if_config.swift:1:1:10:1 | #if ... | 2 | if_config.swift:7:1:7:1 | IfConfigClause | diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.ql b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.ql new file mode 100644 index 00000000000..5ffaf75e476 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/IfConfigDecl_getClause.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from IfConfigDecl x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getClause(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/if_config.swift b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/if_config.swift new file mode 100644 index 00000000000..6f492013933 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/if_config.swift @@ -0,0 +1,10 @@ +#if FOO +var foo: Int = 1 +print("foo") +#elseif BAR +var bar: Int = 2 +print("bar") +#else +var baz: Int = 3 +print("baz") +#endif diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.expected new file mode 100644 index 00000000000..3e9fd295744 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.expected @@ -0,0 +1,5 @@ +| import.swift:1:1:1:8 | import ... | isExported: | no | getModule: | file://:0:0:0:0 | Swift | +| import.swift:2:1:2:24 | import ... | isExported: | no | getModule: | file://:0:0:0:0 | Swift | +| import.swift:3:12:3:32 | import ... | isExported: | yes | getModule: | file://:0:0:0:0 | Swift | +| import.swift:4:1:4:19 | import ... | isExported: | no | getModule: | file://:0:0:0:0 | Swift | +| import.swift:5:1:5:23 | import ... | isExported: | no | getModule: | file://:0:0:0:0 | Swift | diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.ql new file mode 100644 index 00000000000..34978aae118 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl.ql @@ -0,0 +1,11 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ImportDecl x, string isExported, ModuleDecl getModule +where + toBeTested(x) and + not x.isUnknown() and + (if x.isExported() then isExported = "yes" else isExported = "no") and + getModule = x.getModule() +select x, "isExported:", isExported, "getModule:", getModule diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.expected b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.expected new file mode 100644 index 00000000000..b877b113bc6 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.expected @@ -0,0 +1,5 @@ +| import.swift:2:1:2:24 | import ... | 0 | file://:0:0:0:0 | Int | +| import.swift:3:12:3:32 | import ... | 0 | file://:0:0:0:0 | Double | +| import.swift:4:1:4:19 | import ... | 0 | file://:0:0:0:0 | print(_:separator:terminator:) | +| import.swift:4:1:4:19 | import ... | 1 | file://:0:0:0:0 | print(_:separator:terminator:to:) | +| import.swift:5:1:5:23 | import ... | 0 | file://:0:0:0:0 | Hashable | diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.ql b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.ql new file mode 100644 index 00000000000..852fe518ed2 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/ImportDecl_getDeclaration.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ImportDecl x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getDeclaration(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/import.swift b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/import.swift new file mode 100644 index 00000000000..bcb562996ae --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ImportDecl/import.swift @@ -0,0 +1,5 @@ +import Swift +import typealias Swift.Int +@_exported import struct Swift.Double +import func Swift.print // imports all overloads +import protocol Swift.Hashable diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected new file mode 100644 index 00000000000..cfae187857a --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.expected @@ -0,0 +1,2 @@ +| file://:0:0:0:0 | Foo | getInterfaceType: | module | getName: | Foo | isBuiltinModule: | no | isSystemModule: | no | +| file://:0:0:0:0 | default_module_name | getInterfaceType: | module | getName: | default_module_name | isBuiltinModule: | no | isSystemModule: | no | diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.ql new file mode 100644 index 00000000000..73e912a8245 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl.ql @@ -0,0 +1,15 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from + ModuleDecl x, Type getInterfaceType, string getName, string isBuiltinModule, string isSystemModule +where + toBeTested(x) and + not x.isUnknown() and + getInterfaceType = x.getInterfaceType() and + getName = x.getName() and + (if x.isBuiltinModule() then isBuiltinModule = "yes" else isBuiltinModule = "no") and + if x.isSystemModule() then isSystemModule = "yes" else isSystemModule = "no" +select x, "getInterfaceType:", getInterfaceType, "getName:", getName, "isBuiltinModule:", + isBuiltinModule, "isSystemModule:", isSystemModule diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.expected b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.ql b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.ql new file mode 100644 index 00000000000..0c0cec75d86 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/ModuleDecl_getBaseType.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ModuleDecl x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getBaseType(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/default_module_name.swift b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/default_module_name.swift new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/modules.swift b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/modules.swift new file mode 100644 index 00000000000..bb12ed63ddd --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ModuleDecl/modules.swift @@ -0,0 +1 @@ +//codeql-extractor-options: -module-name Foo diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected new file mode 100644 index 00000000000..8f559769407 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.expected @@ -0,0 +1,15 @@ +| param_decls.swift:1:10:1:13 | _ | getInterfaceType: | Int | getName: | _ | getType: | Int | isInout: | no | +| param_decls.swift:1:18:1:29 | y | getInterfaceType: | Double | getName: | y | getType: | Double | isInout: | yes | +| param_decls.swift:2:10:2:13 | _ | getInterfaceType: | Int | getName: | _ | getType: | Int | isInout: | no | +| param_decls.swift:2:18:2:29 | y | getInterfaceType: | Double | getName: | y | getType: | Double | isInout: | yes | +| param_decls.swift:5:5:5:5 | self | getInterfaceType: | S | getName: | self | getType: | S | isInout: | yes | +| param_decls.swift:5:15:5:15 | x | getInterfaceType: | Int | getName: | x | getType: | Int | isInout: | no | +| param_decls.swift:5:15:5:15 | x | getInterfaceType: | Int | getName: | x | getType: | Int | isInout: | no | +| param_decls.swift:5:15:5:18 | x | getInterfaceType: | Int | getName: | x | getType: | Int | isInout: | no | +| param_decls.swift:5:23:5:23 | y | getInterfaceType: | Int | getName: | y | getType: | Int | isInout: | no | +| param_decls.swift:5:23:5:23 | y | getInterfaceType: | Int | getName: | y | getType: | Int | isInout: | no | +| param_decls.swift:5:23:5:26 | y | getInterfaceType: | Int | getName: | y | getType: | Int | isInout: | no | +| param_decls.swift:7:9:7:9 | newValue | getInterfaceType: | Int? | getName: | newValue | getType: | Int? | isInout: | no | +| param_decls.swift:12:13:12:22 | s | getInterfaceType: | String | getName: | s | getType: | String | isInout: | yes | +| param_decls.swift:13:13:13:22 | s | getInterfaceType: | String | getName: | s | getType: | String | isInout: | yes | +| param_decls.swift:14:26:14:26 | $0 | getInterfaceType: | Int | getName: | $0 | getType: | Int | isInout: | no | diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.ql new file mode 100644 index 00000000000..acf3614b7b0 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl.ql @@ -0,0 +1,14 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x, Type getInterfaceType, string getName, Type getType, string isInout +where + toBeTested(x) and + not x.isUnknown() and + getInterfaceType = x.getInterfaceType() and + getName = x.getName() and + getType = x.getType() and + if x.isInout() then isInout = "yes" else isInout = "no" +select x, "getInterfaceType:", getInterfaceType, "getName:", getName, "getType:", getType, + "isInout:", isInout diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.ql new file mode 100644 index 00000000000..a751e0006cc --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAccessorDecl.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getAccessorDecl(index) diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.ql new file mode 100644 index 00000000000..0e71fb1cf26 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getAttachedPropertyWrapperType.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getAttachedPropertyWrapperType() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.ql new file mode 100644 index 00000000000..39f7e9e1b6e --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentInitializer.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getParentInitializer() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.expected b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.ql b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.ql new file mode 100644 index 00000000000..17b5a06c1d5 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/ParamDecl_getParentPattern.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ParamDecl x +where toBeTested(x) and not x.isUnknown() +select x, x.getParentPattern() diff --git a/swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift new file mode 100644 index 00000000000..72b47ed2c60 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/decl/ParamDecl/param_decls.swift @@ -0,0 +1,15 @@ +func foo(_: Int, x y: inout Double) {} +func bar(_: Int, x y: inout Double) {} + +struct S { + subscript(x: Int, y: Int) -> Int? { + get { nil } + set {} + } +} + +func closures() { + let x = {(s: inout String) -> String in s} + let y = {(s: inout String) -> String in ""} + let z : (Int) -> Int = { $0 + 1 } +} diff --git a/swift/ql/test/extractor-tests/generated/expr/DotSyntaxCallExpr/DotSyntaxCallExpr_getArgument.expected b/swift/ql/test/extractor-tests/generated/expr/DotSyntaxCallExpr/DotSyntaxCallExpr_getArgument.expected index 64bdae371b7..85102771b48 100644 --- a/swift/ql/test/extractor-tests/generated/expr/DotSyntaxCallExpr/DotSyntaxCallExpr_getArgument.expected +++ b/swift/ql/test/extractor-tests/generated/expr/DotSyntaxCallExpr/DotSyntaxCallExpr_getArgument.expected @@ -1,2 +1,2 @@ -| dot_syntax_call.swift:6:1:6:3 | call to foo(_:_:) | 0 | : X.Type | -| dot_syntax_call.swift:7:1:7:3 | call to bar() | 0 | : X.Type | +| dot_syntax_call.swift:6:1:6:3 | call to foo(_:_:) | 0 | dot_syntax_call.swift:6:1:6:1 | : X.Type | +| dot_syntax_call.swift:7:1:7:3 | call to bar() | 0 | dot_syntax_call.swift:7:1:7:1 | : X.Type | diff --git a/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.expected b/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.expected index 9b22d5b34d4..6076a256058 100644 --- a/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.expected +++ b/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.expected @@ -1,10 +1,10 @@ -| enum_is_case.swift:4:1:4:17 | ... is some | getSubExpr: | enum_is_case.swift:4:1:4:17 | OptionalEvaluationExpr | getTypeRepr: | enum_is_case.swift:4:22:4:22 | SimpleIdentTypeRepr | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:5:1:5:32 | ... is some | getSubExpr: | enum_is_case.swift:5:1:5:32 | OptionalEvaluationExpr | getTypeRepr: | enum_is_case.swift:5:37:5:37 | SimpleIdentTypeRepr | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:6:1:6:1 | ... is some | getSubExpr: | enum_is_case.swift:6:1:6:1 | 42 | getTypeRepr: | enum_is_case.swift:6:7:6:10 | ...? | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:7:1:7:1 | ... is some | getSubExpr: | enum_is_case.swift:7:1:7:1 | 42 | getTypeRepr: | enum_is_case.swift:7:7:7:11 | ...? | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:9:1:9:19 | ... is some | getSubExpr: | enum_is_case.swift:9:1:9:19 | [...] | getTypeRepr: | enum_is_case.swift:9:24:9:28 | [...] | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:19:1:19:18 | ... is some | getSubExpr: | enum_is_case.swift:19:1:19:18 | OptionalEvaluationExpr | getTypeRepr: | enum_is_case.swift:19:23:19:23 | SimpleIdentTypeRepr | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:21:1:21:5 | ... is some | getSubExpr: | enum_is_case.swift:21:1:21:5 | [...] | getTypeRepr: | enum_is_case.swift:21:10:21:12 | [...] | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:22:1:22:10 | ... is some | getSubExpr: | enum_is_case.swift:22:1:22:10 | [...] | getTypeRepr: | enum_is_case.swift:22:15:22:25 | [... : ...] | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:23:1:23:10 | ... is some | getSubExpr: | enum_is_case.swift:23:1:23:10 | [...] | getTypeRepr: | enum_is_case.swift:23:15:23:25 | [... : ...] | getElement: | file://:0:0:0:0 | some | -| enum_is_case.swift:24:1:24:8 | ... is some | getSubExpr: | enum_is_case.swift:24:1:24:8 | call to ... | getTypeRepr: | enum_is_case.swift:24:13:24:18 | ...<...> | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:4:1:4:17 | ... is some | getSubExpr: | enum_is_case.swift:4:1:4:17 | OptionalEvaluationExpr | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:5:1:5:32 | ... is some | getSubExpr: | enum_is_case.swift:5:1:5:32 | OptionalEvaluationExpr | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:6:1:6:1 | ... is some | getSubExpr: | enum_is_case.swift:6:1:6:1 | 42 | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:7:1:7:1 | ... is some | getSubExpr: | enum_is_case.swift:7:1:7:1 | 42 | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:9:1:9:19 | ... is some | getSubExpr: | enum_is_case.swift:9:1:9:19 | [...] | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:19:1:19:18 | ... is some | getSubExpr: | enum_is_case.swift:19:1:19:18 | OptionalEvaluationExpr | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:21:1:21:5 | ... is some | getSubExpr: | enum_is_case.swift:21:1:21:5 | [...] | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:22:1:22:10 | ... is some | getSubExpr: | enum_is_case.swift:22:1:22:10 | [...] | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:23:1:23:10 | ... is some | getSubExpr: | enum_is_case.swift:23:1:23:10 | [...] | getElement: | file://:0:0:0:0 | some | +| enum_is_case.swift:24:1:24:8 | ... is some | getSubExpr: | enum_is_case.swift:24:1:24:8 | call to ... | getElement: | file://:0:0:0:0 | some | diff --git a/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.ql b/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.ql index e35700dcbe0..9c3340d4ff5 100644 --- a/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.ql +++ b/swift/ql/test/extractor-tests/generated/expr/EnumIsCaseExpr/EnumIsCaseExpr.ql @@ -2,11 +2,10 @@ import codeql.swift.elements import TestUtils -from EnumIsCaseExpr x, Expr getSubExpr, TypeRepr getTypeRepr, EnumElementDecl getElement +from EnumIsCaseExpr x, Expr getSubExpr, EnumElementDecl getElement where toBeTested(x) and not x.isUnknown() and getSubExpr = x.getSubExpr() and - getTypeRepr = x.getTypeRepr() and getElement = x.getElement() -select x, "getSubExpr:", getSubExpr, "getTypeRepr:", getTypeRepr, "getElement:", getElement +select x, "getSubExpr:", getSubExpr, "getElement:", getElement diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.expected new file mode 100644 index 00000000000..ecf2a57ba5d --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.expected @@ -0,0 +1,10 @@ +| unresolved_decl_ref.swift:4:5:4:5 | FOO (unresolved) | +| unresolved_decl_ref.swift:4:9:4:9 | && (unresolved) | +| unresolved_decl_ref.swift:4:12:4:12 | os (unresolved) | +| unresolved_decl_ref.swift:4:15:4:15 | Windows (unresolved) | +| unresolved_decl_ref.swift:5:1:5:1 | print (unresolved) | +| unresolved_decl_ref.swift:6:9:6:9 | BAR (unresolved) | +| unresolved_decl_ref.swift:6:13:6:13 | \|\| (unresolved) | +| unresolved_decl_ref.swift:6:16:6:16 | arch (unresolved) | +| unresolved_decl_ref.swift:6:21:6:21 | i386 (unresolved) | +| unresolved_decl_ref.swift:9:1:9:1 | print (unresolved) | diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.ql new file mode 100644 index 00000000000..507d2fcf87d --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from UnresolvedDeclRefExpr x +where toBeTested(x) and not x.isUnknown() +select x diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.expected new file mode 100644 index 00000000000..1ea60aabedc --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.expected @@ -0,0 +1,10 @@ +| unresolved_decl_ref.swift:4:5:4:5 | FOO (unresolved) | FOO | +| unresolved_decl_ref.swift:4:9:4:9 | && (unresolved) | && | +| unresolved_decl_ref.swift:4:12:4:12 | os (unresolved) | os | +| unresolved_decl_ref.swift:4:15:4:15 | Windows (unresolved) | Windows | +| unresolved_decl_ref.swift:5:1:5:1 | print (unresolved) | print | +| unresolved_decl_ref.swift:6:9:6:9 | BAR (unresolved) | BAR | +| unresolved_decl_ref.swift:6:13:6:13 | \|\| (unresolved) | \|\| | +| unresolved_decl_ref.swift:6:16:6:16 | arch (unresolved) | arch | +| unresolved_decl_ref.swift:6:21:6:21 | i386 (unresolved) | i386 | +| unresolved_decl_ref.swift:9:1:9:1 | print (unresolved) | print | diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.ql new file mode 100644 index 00000000000..1424e3443d9 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getName.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from UnresolvedDeclRefExpr x +where toBeTested(x) and not x.isUnknown() +select x, x.getName() diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.ql similarity index 83% rename from swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.ql rename to swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.ql index f91cc957ef7..27953cceeb1 100644 --- a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr_getType.ql +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/UnresolvedDeclRefExpr_getType.ql @@ -2,6 +2,6 @@ import codeql.swift.elements import TestUtils -from UnresolvedDotExpr x +from UnresolvedDeclRefExpr x where toBeTested(x) and not x.isUnknown() select x, x.getType() diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/unresolved_decl_ref.swift b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/unresolved_decl_ref.swift new file mode 100644 index 00000000000..94dee19d618 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDeclRefExpr/unresolved_decl_ref.swift @@ -0,0 +1,10 @@ +//codeql-extractor-options: -D BAR + +// conditions and inactive branches in conditional compilation blocks are not resolved +#if FOO && os(Windows) +print(1) +#elseif BAR || arch(i386) +print(2) +#else +print(3) +#endif diff --git a/swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/MISSING_SOURCE.txt similarity index 100% rename from swift/ql/test/extractor-tests/generated/decl/IfConfigDecl/MISSING_SOURCE.txt rename to swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/MISSING_SOURCE.txt diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.expected deleted file mode 100644 index 2bb615eb361..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.expected +++ /dev/null @@ -1,3 +0,0 @@ -| unresolved_dot_expr.swift:5:6:5:8 | ... .x | getBase: | unresolved_dot_expr.swift:5:6:5:6 | (no string representation) | getName: | x | -| unresolved_dot_expr.swift:11:6:11:8 | ... .a | getBase: | unresolved_dot_expr.swift:11:6:11:6 | (no string representation) | getName: | a | -| unresolved_dot_expr.swift:11:6:11:10 | ... .x | getBase: | unresolved_dot_expr.swift:11:6:11:8 | ... .a | getName: | x | diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.ql deleted file mode 100644 index 29327a3f86c..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/UnresolvedDotExpr.ql +++ /dev/null @@ -1,11 +0,0 @@ -// generated by codegen/codegen.py -import codeql.swift.elements -import TestUtils - -from UnresolvedDotExpr x, Expr getBase, string getName -where - toBeTested(x) and - not x.isUnknown() and - getBase = x.getBase() and - getName = x.getName() -select x, "getBase:", getBase, "getName:", getName diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/unresolved_dot_expr.swift b/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/unresolved_dot_expr.swift deleted file mode 100644 index 3003e6efe82..00000000000 --- a/swift/ql/test/extractor-tests/generated/expr/UnresolvedDotExpr/unresolved_dot_expr.swift +++ /dev/null @@ -1,11 +0,0 @@ -struct A { - var x: Int = 42 -} - -_ = \A.x - -struct B { - var a: A -} - -_ = \B.a.x diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.expected new file mode 100644 index 00000000000..3f348092bdb --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.expected @@ -0,0 +1 @@ +| unresolved_pattern_expr.swift:2:19:2:19 | UnresolvedPatternExpr | getSubPattern: | unresolved_pattern_expr.swift:2:19:2:19 | x | diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.ql new file mode 100644 index 00000000000..9a5f8e54490 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr.ql @@ -0,0 +1,10 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from UnresolvedPatternExpr x, Pattern getSubPattern +where + toBeTested(x) and + not x.isUnknown() and + getSubPattern = x.getSubPattern() +select x, "getSubPattern:", getSubPattern diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.expected b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.ql b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.ql new file mode 100644 index 00000000000..af57d0129a1 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/UnresolvedPatternExpr_getType.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from UnresolvedPatternExpr x +where toBeTested(x) and not x.isUnknown() +select x, x.getType() diff --git a/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/unresolved_pattern_expr.swift b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/unresolved_pattern_expr.swift new file mode 100644 index 00000000000..2ca1bbb7c24 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/expr/UnresolvedPatternExpr/unresolved_pattern_expr.swift @@ -0,0 +1,5 @@ +#if FOO +if case let .some(x) = 42 { + print(x) +} +#endif diff --git a/swift/ql/test/extractor-tests/generated/type/ModuleType/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/type/ModuleType/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/ModuleType/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.expected b/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.expected new file mode 100644 index 00000000000..3a688c7fbd6 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.expected @@ -0,0 +1,2 @@ +| module | getName: | module | getCanonicalType: | module | getModule: | file://:0:0:0:0 | Foo | +| module | getName: | module | getCanonicalType: | module | getModule: | file://:0:0:0:0 | default_module_name | diff --git a/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.ql b/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.ql new file mode 100644 index 00000000000..aa070b52e34 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ModuleType/ModuleType.ql @@ -0,0 +1,12 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from ModuleType x, string getName, Type getCanonicalType, ModuleDecl getModule +where + toBeTested(x) and + not x.isUnknown() and + getName = x.getName() and + getCanonicalType = x.getCanonicalType() and + getModule = x.getModule() +select x, "getName:", getName, "getCanonicalType:", getCanonicalType, "getModule:", getModule diff --git a/swift/ql/test/extractor-tests/generated/type/ModuleType/default_module_name.swift b/swift/ql/test/extractor-tests/generated/type/ModuleType/default_module_name.swift new file mode 100644 index 00000000000..e69de29bb2d diff --git a/swift/ql/test/extractor-tests/generated/type/ModuleType/modules.swift b/swift/ql/test/extractor-tests/generated/type/ModuleType/modules.swift new file mode 100644 index 00000000000..bb12ed63ddd --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/ModuleType/modules.swift @@ -0,0 +1 @@ +//codeql-extractor-options: -module-name Foo diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.expected b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.expected new file mode 100644 index 00000000000..f6c15bca419 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.expected @@ -0,0 +1 @@ +| C & P1 & P2 | getName: | C & P1 & P2 | getCanonicalType: | C & P1 & P2 | getInterfaceType: | \u03c4_0_0 | diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.ql b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.ql new file mode 100644 index 00000000000..b558c08f666 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType.ql @@ -0,0 +1,13 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from OpenedArchetypeType x, string getName, Type getCanonicalType, Type getInterfaceType +where + toBeTested(x) and + not x.isUnknown() and + getName = x.getName() and + getCanonicalType = x.getCanonicalType() and + getInterfaceType = x.getInterfaceType() +select x, "getName:", getName, "getCanonicalType:", getCanonicalType, "getInterfaceType:", + getInterfaceType diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.expected b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.expected new file mode 100644 index 00000000000..5899ea9308a --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.expected @@ -0,0 +1,2 @@ +| C & P1 & P2 | 0 | opened_archetypes.swift:3:1:3:14 | P1 | +| C & P1 & P2 | 1 | opened_archetypes.swift:9:1:9:14 | P2 | diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.ql b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.ql new file mode 100644 index 00000000000..58fed5dda7b --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getProtocol.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from OpenedArchetypeType x, int index +where toBeTested(x) and not x.isUnknown() +select x, index, x.getProtocol(index) diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.expected b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.expected new file mode 100644 index 00000000000..be1cb7dcb05 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.expected @@ -0,0 +1 @@ +| C & P1 & P2 | C | diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.ql b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.ql new file mode 100644 index 00000000000..46f00a08c88 --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/OpenedArchetypeType_getSuperclass.ql @@ -0,0 +1,7 @@ +// generated by codegen/codegen.py +import codeql.swift.elements +import TestUtils + +from OpenedArchetypeType x +where toBeTested(x) and not x.isUnknown() +select x, x.getSuperclass() diff --git a/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/opened_archetypes.swift b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/opened_archetypes.swift new file mode 100644 index 00000000000..93d58b6163f --- /dev/null +++ b/swift/ql/test/extractor-tests/generated/type/OpenedArchetypeType/opened_archetypes.swift @@ -0,0 +1,25 @@ +// code inspired by https://github.com/apple/swift-evolution/blob/main/proposals/0352-implicit-open-existentials.md + +protocol P1 {} + +func isFoo(_: T) -> Bool { + return true +} + +protocol P2 {} + +class C {} + +// will be ok with swift 5.7 +// func test(value: any P1 & P2 & C) -> Bool { return isFoo(value) } + +extension P1 { + var isFooMember: Bool { + isFoo(self) + } +} + + +func testMember(value: any P1 & P2 & C) -> Bool { + return value.isFooMember // here the existential type is implicitly "opened" +} diff --git a/swift/ql/test/extractor-tests/generated/decl/ImportDecl/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/type/TypeRepr/MISSING_SOURCE.txt similarity index 100% rename from swift/ql/test/extractor-tests/generated/decl/ImportDecl/MISSING_SOURCE.txt rename to swift/ql/test/extractor-tests/generated/type/TypeRepr/MISSING_SOURCE.txt diff --git a/swift/ql/test/extractor-tests/generated/typerepr/ArrayTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/ArrayTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/ArrayTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/AttributedTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/AttributedTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/AttributedTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/CompileTimeConstTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/CompileTimeConstTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/CompileTimeConstTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/CompositionTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/CompositionTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/CompositionTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/CompoundIdentTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/CompoundIdentTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/CompoundIdentTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/DictionaryTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/DictionaryTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/DictionaryTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/ErrorTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/ErrorTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/ErrorTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/ExistentialTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/ExistentialTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/ExistentialTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/FixedTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/FixedTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/FixedTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/FunctionTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/FunctionTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/FunctionTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/GenericIdentTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/GenericIdentTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/GenericIdentTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/ImplicitlyUnwrappedOptionalTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/InOutTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/InOutTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/InOutTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/IsolatedTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/IsolatedTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/IsolatedTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/MetatypeTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/MetatypeTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/MetatypeTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/NamedOpaqueReturnTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/NamedOpaqueReturnTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/NamedOpaqueReturnTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/OpaqueReturnTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/OpaqueReturnTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/OpaqueReturnTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/OptionalTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/OptionalTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/OptionalTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/OwnedTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/OwnedTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/OwnedTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/PlaceholderTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/PlaceholderTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/PlaceholderTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/ProtocolTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/ProtocolTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/ProtocolTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/SharedTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/SharedTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/SharedTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/SilBoxTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/SilBoxTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/SilBoxTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/SimpleIdentTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/SimpleIdentTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/SimpleIdentTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/extractor-tests/generated/typerepr/TupleTypeRepr/MISSING_SOURCE.txt b/swift/ql/test/extractor-tests/generated/typerepr/TupleTypeRepr/MISSING_SOURCE.txt deleted file mode 100644 index 0d319d9a669..00000000000 --- a/swift/ql/test/extractor-tests/generated/typerepr/TupleTypeRepr/MISSING_SOURCE.txt +++ /dev/null @@ -1,4 +0,0 @@ -// generated by codegen/codegen.py - -After a swift source file is added in this directory and codegen/codegen.py is run again, test queries -will appear and this file will be deleted diff --git a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected index 4d38d88d82c..c6e8d620e85 100644 --- a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -200,6 +200,7 @@ cfg.swift: #-----| -> Did not throw. # 29| call to print(_:separator:terminator:) +#-----| -> mightThrow(x:) # 29| default separator #-----| -> default terminator @@ -219,6 +220,43 @@ cfg.swift: # 29| [...] #-----| -> [...] +# 30| try! ... +#-----| -> print(_:separator:terminator:) + +# 30| mightThrow(x:) +#-----| -> 0 + +# 30| call to mightThrow(x:) +#-----| -> try! ... +#-----| exception -> case ... + +# 30| 0 +#-----| -> call to mightThrow(x:) + +# 31| print(_:separator:terminator:) +#-----| -> Still did not throw. + +# 31| call to print(_:separator:terminator:) +#-----| -> 0 + +# 31| default separator +#-----| -> default terminator + +# 31| default terminator +#-----| -> call to print(_:separator:terminator:) + +# 31| (Any) ... +#-----| -> [...] + +# 31| Still did not throw. +#-----| -> (Any) ... + +# 31| [...] +#-----| -> default separator + +# 31| [...] +#-----| -> [...] + # 33| case ... #-----| -> ... is ... @@ -281,15 +319,12 @@ cfg.swift: #-----| -> ... is ... # 37| ... is ... -#-----| -> SimpleIdentTypeRepr +#-----| -> ... is ... # 37| ... is ... #-----| match -> print(_:separator:terminator:) #-----| no-match -> case ... -# 37| SimpleIdentTypeRepr -#-----| -> ... is ... - # 38| print(_:separator:terminator:) #-----| -> MyError @@ -327,7 +362,6 @@ cfg.swift: #-----| -> error # 40| print(_:separator:terminator:) -#-----| -> "..." # 40| call to print(_:separator:terminator:) #-----| -> 0 @@ -344,12 +378,64 @@ cfg.swift: # 40| (Any) ... #-----| -> [...] +# 40| OpaqueValueExpr + +# 40| TapExpr +#-----| -> "..." + +# 40| Unknown error +#-----| -> call to ... + # 40| [...] #-----| -> default separator # 40| [...] #-----| -> [...] +# 40| call to ... +#-----| -> appendInterpolation(_:) + +# 40| $interpolation +#-----| -> &... + +# 40| &... +#-----| -> call to appendLiteral(_:) + +# 40| call to appendLiteral(_:) +#-----| -> Unknown error + +# 40| $interpolation +#-----| -> &... + +# 40| &... +#-----| -> call to appendInterpolation(_:) + +# 40| appendInterpolation(_:) +#-----| -> $interpolation + +# 40| call to appendInterpolation(_:) +#-----| -> error + +# 40| call to ... + +# 40| error +#-----| -> call to ... + +# 40| +#-----| -> call to ... + +# 40| $interpolation +#-----| -> &... + +# 40| &... +#-----| -> call to appendLiteral(_:) + +# 40| call to ... +#-----| -> TapExpr + +# 40| call to appendLiteral(_:) +#-----| -> + # 42| return ... #-----| return -> exit tryCatch(x:) (normal) @@ -2820,14 +2906,179 @@ cfg.swift: #-----| -> y # 262| y -#-----| -> "..." # 263| return ... #-----| return -> exit interpolatedString(x:y:) (normal) +# 263| +#-----| -> call to ... + # 263| "..." #-----| -> return ... +# 263| OpaqueValueExpr + +# 263| TapExpr +#-----| -> "..." + +# 263| call to ... +#-----| -> appendInterpolation(_:) + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendLiteral(_:) + +# 263| call to appendLiteral(_:) +#-----| -> + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendInterpolation(_:) + +# 263| appendInterpolation(_:) +#-----| -> $interpolation + +# 263| call to appendInterpolation(_:) +#-----| -> x + +# 263| call to ... + +# 263| x +#-----| -> call to ... + +# 263| + +#-----| -> call to ... + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendLiteral(_:) + +# 263| call to ... +#-----| -> appendInterpolation(_:) + +# 263| call to appendLiteral(_:) +#-----| -> + + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendInterpolation(_:) + +# 263| appendInterpolation(_:) +#-----| -> $interpolation + +# 263| call to appendInterpolation(_:) +#-----| -> y + +# 263| call to ... + +# 263| y +#-----| -> call to ... + +# 263| is equal to +#-----| -> call to ... + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendLiteral(_:) + +# 263| call to ... +#-----| -> appendInterpolation(_:) + +# 263| call to appendLiteral(_:) +#-----| -> is equal to + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendInterpolation(_:) + +# 263| appendInterpolation(_:) +#-----| -> $interpolation + +# 263| call to appendInterpolation(_:) +#-----| -> +(_:_:) + +# 263| call to ... + +# 263| x +#-----| -> y + +# 263| ... call to +(_:_:) ... +#-----| -> call to ... + +# 263| +(_:_:) +#-----| -> Int.Type + +# 263| Int.Type +#-----| -> call to +(_:_:) + +# 263| call to +(_:_:) +#-----| -> x + +# 263| y +#-----| -> ... call to +(_:_:) ... + +# 263| and here is a zero: +#-----| -> call to ... + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendLiteral(_:) + +# 263| call to ... +#-----| -> appendInterpolation(_:) + +# 263| call to appendLiteral(_:) +#-----| -> and here is a zero: + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendInterpolation(_:) + +# 263| appendInterpolation(_:) +#-----| -> $interpolation + +# 263| call to appendInterpolation(_:) +#-----| -> returnZero() + +# 263| call to ... + +# 263| returnZero() +#-----| -> call to returnZero() + +# 263| call to returnZero() +#-----| -> call to ... + +# 263| +#-----| -> call to ... + +# 263| $interpolation +#-----| -> &... + +# 263| &... +#-----| -> call to appendLiteral(_:) + +# 263| call to ... +#-----| -> TapExpr + +# 263| call to appendLiteral(_:) +#-----| -> + # 266| enter testSubscriptExpr() #-----| -> testSubscriptExpr() @@ -5093,6 +5344,7 @@ cfg.swift: #-----| -> Did not throw. # 386| call to print(_:separator:terminator:) +#-----| -> mightThrow(x:) # 386| default separator #-----| -> default terminator @@ -5112,6 +5364,49 @@ cfg.swift: # 386| [...] #-----| -> [...] +# 387| try! ... +#-----| -> print(_:separator:terminator:) + +# 387| mightThrow(x:) +#-----| -> 0 + +# 387| call to mightThrow(x:) +#-----| exception -> exit doWithoutCatch(x:) (normal) +#-----| -> try! ... + +# 387| 0 +#-----| -> call to mightThrow(x:) + +# 388| print(_:separator:terminator:) +#-----| -> Still did not throw. + +# 388| call to print(_:separator:terminator:) +#-----| -> 0 + +# 388| default separator +#-----| -> default terminator + +# 388| default terminator +#-----| -> call to print(_:separator:terminator:) + +# 388| (Any) ... +#-----| -> [...] + +# 388| Still did not throw. +#-----| -> (Any) ... + +# 388| [...] +#-----| -> default separator + +# 388| [...] +#-----| -> [...] + +# 390| return ... +#-----| return -> exit doWithoutCatch(x:) (normal) + +# 390| 0 +#-----| -> return ... + # 394| (unnamed function decl) # 394| enter (unnamed function decl) @@ -5575,15 +5870,39 @@ cfg.swift: # 456| var ... = ... #-----| -> kpGet_b_x +# 456| var ... = ... +#-----| -> kpGet_b_x + # 456| kpGet_b_x #-----| match -> #keyPath(...) # 456| kpGet_b_x #-----| -> kpGet_bs_0_x +# 456| kpGet_b_x +#-----| -> kpGet_bs_0_x + # 456| #keyPath(...) #-----| -> var ... = ... +# 456| #keyPath(...) +#-----| -> var ... = ... +#-----| -> exit #keyPath(...) (normal) + +# 456| enter #keyPath(...) +#-----| -> #keyPath(...) + +# 456| exit #keyPath(...) + +# 456| exit #keyPath(...) (normal) +#-----| -> exit #keyPath(...) + +# 457| var ... = ... +#-----| -> kpGet_bs_0_x + +# 457| var ... = ... +#-----| -> kpGet_bs_0_x + # 457| var ... = ... #-----| -> kpGet_bs_0_x @@ -5593,9 +5912,42 @@ cfg.swift: # 457| kpGet_bs_0_x #-----| -> kpGet_mayB_force_x +# 457| kpGet_bs_0_x +#-----| match -> #keyPath(...) + +# 457| kpGet_bs_0_x +#-----| -> kpGet_mayB_force_x + +# 457| kpGet_bs_0_x +#-----| -> kpGet_mayB_force_x + # 457| #keyPath(...) #-----| -> var ... = ... +# 457| #keyPath(...) +#-----| -> var ... = ... + +# 457| #keyPath(...) +#-----| -> var ... = ... +#-----| -> exit #keyPath(...) (normal) + +# 457| enter #keyPath(...) +#-----| -> #keyPath(...) + +# 457| exit #keyPath(...) + +# 457| exit #keyPath(...) (normal) +#-----| -> exit #keyPath(...) + +# 458| var ... = ... +#-----| -> kpGet_mayB_force_x + +# 458| var ... = ... +#-----| -> kpGet_mayB_force_x + +# 458| var ... = ... +#-----| -> kpGet_mayB_force_x + # 458| var ... = ... #-----| -> kpGet_mayB_force_x @@ -5605,9 +5957,54 @@ cfg.swift: # 458| kpGet_mayB_force_x #-----| -> kpGet_mayB_x +# 458| kpGet_mayB_force_x +#-----| match -> #keyPath(...) + +# 458| kpGet_mayB_force_x +#-----| -> kpGet_mayB_x + +# 458| kpGet_mayB_force_x +#-----| match -> #keyPath(...) + +# 458| kpGet_mayB_force_x +#-----| -> kpGet_mayB_x + +# 458| kpGet_mayB_force_x +#-----| -> kpGet_mayB_x + # 458| #keyPath(...) #-----| -> var ... = ... +# 458| #keyPath(...) +#-----| -> var ... = ... + +# 458| #keyPath(...) +#-----| -> var ... = ... + +# 458| #keyPath(...) +#-----| -> var ... = ... +#-----| -> exit #keyPath(...) (normal) + +# 458| enter #keyPath(...) +#-----| -> #keyPath(...) + +# 458| exit #keyPath(...) + +# 458| exit #keyPath(...) (normal) +#-----| -> exit #keyPath(...) + +# 459| var ... = ... +#-----| -> kpGet_mayB_x + +# 459| var ... = ... +#-----| -> kpGet_mayB_x + +# 459| var ... = ... +#-----| -> kpGet_mayB_x + +# 459| var ... = ... +#-----| -> kpGet_mayB_x + # 459| var ... = ... #-----| -> kpGet_mayB_x @@ -5617,9 +6014,63 @@ cfg.swift: # 459| kpGet_mayB_x #-----| -> apply_kpGet_b_x +# 459| kpGet_mayB_x +#-----| match -> #keyPath(...) + +# 459| kpGet_mayB_x +#-----| -> apply_kpGet_b_x + +# 459| kpGet_mayB_x +#-----| match -> #keyPath(...) + +# 459| kpGet_mayB_x +#-----| -> apply_kpGet_b_x + +# 459| kpGet_mayB_x +#-----| match -> #keyPath(...) + +# 459| kpGet_mayB_x +#-----| -> apply_kpGet_b_x + +# 459| kpGet_mayB_x +#-----| -> apply_kpGet_b_x + # 459| #keyPath(...) #-----| -> var ... = ... +# 459| #keyPath(...) +#-----| -> var ... = ... + +# 459| #keyPath(...) +#-----| -> var ... = ... + +# 459| #keyPath(...) +#-----| -> var ... = ... + +# 459| #keyPath(...) +#-----| -> var ... = ... +#-----| -> exit #keyPath(...) (normal) + +# 459| enter #keyPath(...) +#-----| -> #keyPath(...) + +# 459| exit #keyPath(...) + +# 459| exit #keyPath(...) (normal) +#-----| -> exit #keyPath(...) + +# 461| var ... = ... +#-----| -> apply_kpGet_b_x + +# 461| var ... = ... +#-----| -> apply_kpGet_b_x + +# 461| var ... = ... +#-----| -> apply_kpGet_b_x + +# 461| var ... = ... +#-----| -> apply_kpGet_b_x + # 461| var ... = ... #-----| -> apply_kpGet_b_x @@ -5629,18 +6080,102 @@ cfg.swift: # 461| apply_kpGet_b_x #-----| -> apply_kpGet_bs_0_x +# 461| apply_kpGet_b_x +#-----| match -> a + +# 461| apply_kpGet_b_x +#-----| -> apply_kpGet_bs_0_x + +# 461| apply_kpGet_b_x +#-----| match -> a + +# 461| apply_kpGet_b_x +#-----| -> apply_kpGet_bs_0_x + +# 461| apply_kpGet_b_x +#-----| match -> a + +# 461| apply_kpGet_b_x +#-----| -> apply_kpGet_bs_0_x + +# 461| apply_kpGet_b_x +#-----| match -> a + +# 461| apply_kpGet_b_x +#-----| -> apply_kpGet_bs_0_x + # 461| a #-----| -> kpGet_b_x +# 461| a +#-----| -> kpGet_b_x + +# 461| a +#-----| -> kpGet_b_x + +# 461| a +#-----| -> kpGet_b_x + +# 461| a +#-----| -> kpGet_b_x + +# 461| \...[...] +#-----| -> var ... = ... + +# 461| \...[...] +#-----| -> var ... = ... + +# 461| \...[...] +#-----| -> var ... = ... + +# 461| \...[...] +#-----| -> var ... = ... + # 461| \...[...] #-----| -> var ... = ... # 461| (WritableKeyPath) ... #-----| -> \...[...] +# 461| (WritableKeyPath) ... +#-----| -> \...[...] + +# 461| (WritableKeyPath) ... +#-----| -> \...[...] + +# 461| (WritableKeyPath) ... +#-----| -> \...[...] + +# 461| (WritableKeyPath) ... +#-----| -> \...[...] + # 461| kpGet_b_x #-----| -> (WritableKeyPath) ... +# 461| kpGet_b_x +#-----| -> (WritableKeyPath) ... + +# 461| kpGet_b_x +#-----| -> (WritableKeyPath) ... + +# 461| kpGet_b_x +#-----| -> (WritableKeyPath) ... + +# 461| kpGet_b_x +#-----| -> (WritableKeyPath) ... + +# 462| var ... = ... +#-----| -> apply_kpGet_bs_0_x + +# 462| var ... = ... +#-----| -> apply_kpGet_bs_0_x + +# 462| var ... = ... +#-----| -> apply_kpGet_bs_0_x + +# 462| var ... = ... +#-----| -> apply_kpGet_bs_0_x + # 462| var ... = ... #-----| -> apply_kpGet_bs_0_x @@ -5650,18 +6185,102 @@ cfg.swift: # 462| apply_kpGet_bs_0_x #-----| -> apply_kpGet_mayB_force_x +# 462| apply_kpGet_bs_0_x +#-----| match -> a + +# 462| apply_kpGet_bs_0_x +#-----| -> apply_kpGet_mayB_force_x + +# 462| apply_kpGet_bs_0_x +#-----| match -> a + +# 462| apply_kpGet_bs_0_x +#-----| -> apply_kpGet_mayB_force_x + +# 462| apply_kpGet_bs_0_x +#-----| match -> a + +# 462| apply_kpGet_bs_0_x +#-----| -> apply_kpGet_mayB_force_x + +# 462| apply_kpGet_bs_0_x +#-----| match -> a + +# 462| apply_kpGet_bs_0_x +#-----| -> apply_kpGet_mayB_force_x + # 462| a #-----| -> kpGet_bs_0_x +# 462| a +#-----| -> kpGet_bs_0_x + +# 462| a +#-----| -> kpGet_bs_0_x + +# 462| a +#-----| -> kpGet_bs_0_x + +# 462| a +#-----| -> kpGet_bs_0_x + +# 462| \...[...] +#-----| -> var ... = ... + +# 462| \...[...] +#-----| -> var ... = ... + +# 462| \...[...] +#-----| -> var ... = ... + +# 462| \...[...] +#-----| -> var ... = ... + # 462| \...[...] #-----| -> var ... = ... # 462| (WritableKeyPath) ... #-----| -> \...[...] +# 462| (WritableKeyPath) ... +#-----| -> \...[...] + +# 462| (WritableKeyPath) ... +#-----| -> \...[...] + +# 462| (WritableKeyPath) ... +#-----| -> \...[...] + +# 462| (WritableKeyPath) ... +#-----| -> \...[...] + # 462| kpGet_bs_0_x #-----| -> (WritableKeyPath) ... +# 462| kpGet_bs_0_x +#-----| -> (WritableKeyPath) ... + +# 462| kpGet_bs_0_x +#-----| -> (WritableKeyPath) ... + +# 462| kpGet_bs_0_x +#-----| -> (WritableKeyPath) ... + +# 462| kpGet_bs_0_x +#-----| -> (WritableKeyPath) ... + +# 463| var ... = ... +#-----| -> apply_kpGet_mayB_force_x + +# 463| var ... = ... +#-----| -> apply_kpGet_mayB_force_x + +# 463| var ... = ... +#-----| -> apply_kpGet_mayB_force_x + +# 463| var ... = ... +#-----| -> apply_kpGet_mayB_force_x + # 463| var ... = ... #-----| -> apply_kpGet_mayB_force_x @@ -5671,18 +6290,102 @@ cfg.swift: # 463| apply_kpGet_mayB_force_x #-----| -> apply_kpGet_mayB_x +# 463| apply_kpGet_mayB_force_x +#-----| match -> a + +# 463| apply_kpGet_mayB_force_x +#-----| -> apply_kpGet_mayB_x + +# 463| apply_kpGet_mayB_force_x +#-----| match -> a + +# 463| apply_kpGet_mayB_force_x +#-----| -> apply_kpGet_mayB_x + +# 463| apply_kpGet_mayB_force_x +#-----| match -> a + +# 463| apply_kpGet_mayB_force_x +#-----| -> apply_kpGet_mayB_x + +# 463| apply_kpGet_mayB_force_x +#-----| match -> a + +# 463| apply_kpGet_mayB_force_x +#-----| -> apply_kpGet_mayB_x + # 463| a #-----| -> kpGet_mayB_force_x +# 463| a +#-----| -> kpGet_mayB_force_x + +# 463| a +#-----| -> kpGet_mayB_force_x + +# 463| a +#-----| -> kpGet_mayB_force_x + +# 463| a +#-----| -> kpGet_mayB_force_x + +# 463| \...[...] +#-----| -> var ... = ... + +# 463| \...[...] +#-----| -> var ... = ... + +# 463| \...[...] +#-----| -> var ... = ... + +# 463| \...[...] +#-----| -> var ... = ... + # 463| \...[...] #-----| -> var ... = ... # 463| (WritableKeyPath) ... #-----| -> \...[...] +# 463| (WritableKeyPath) ... +#-----| -> \...[...] + +# 463| (WritableKeyPath) ... +#-----| -> \...[...] + +# 463| (WritableKeyPath) ... +#-----| -> \...[...] + +# 463| (WritableKeyPath) ... +#-----| -> \...[...] + # 463| kpGet_mayB_force_x #-----| -> (WritableKeyPath) ... +# 463| kpGet_mayB_force_x +#-----| -> (WritableKeyPath) ... + +# 463| kpGet_mayB_force_x +#-----| -> (WritableKeyPath) ... + +# 463| kpGet_mayB_force_x +#-----| -> (WritableKeyPath) ... + +# 463| kpGet_mayB_force_x +#-----| -> (WritableKeyPath) ... + +# 464| var ... = ... +#-----| -> apply_kpGet_mayB_x + +# 464| var ... = ... +#-----| -> apply_kpGet_mayB_x + +# 464| var ... = ... +#-----| -> apply_kpGet_mayB_x + +# 464| var ... = ... +#-----| -> apply_kpGet_mayB_x + # 464| var ... = ... #-----| -> apply_kpGet_mayB_x @@ -5692,14 +6395,82 @@ cfg.swift: # 464| apply_kpGet_mayB_x #-----| -> exit test(a:) (normal) +# 464| apply_kpGet_mayB_x +#-----| match -> a + +# 464| apply_kpGet_mayB_x + +# 464| apply_kpGet_mayB_x +#-----| match -> a + +# 464| apply_kpGet_mayB_x + +# 464| apply_kpGet_mayB_x +#-----| match -> a + +# 464| apply_kpGet_mayB_x + +# 464| apply_kpGet_mayB_x +#-----| match -> a + +# 464| apply_kpGet_mayB_x + # 464| a #-----| -> kpGet_mayB_x +# 464| a +#-----| -> kpGet_mayB_x + +# 464| a +#-----| -> kpGet_mayB_x + +# 464| a +#-----| -> kpGet_mayB_x + +# 464| a +#-----| -> kpGet_mayB_x + +# 464| \...[...] +#-----| -> var ... = ... + +# 464| \...[...] +#-----| -> var ... = ... + +# 464| \...[...] +#-----| -> var ... = ... + +# 464| \...[...] +#-----| -> var ... = ... + # 464| \...[...] #-----| -> var ... = ... # 464| (KeyPath) ... #-----| -> \...[...] +# 464| (KeyPath) ... +#-----| -> \...[...] + +# 464| (KeyPath) ... +#-----| -> \...[...] + +# 464| (KeyPath) ... +#-----| -> \...[...] + +# 464| (KeyPath) ... +#-----| -> \...[...] + +# 464| kpGet_mayB_x +#-----| -> (KeyPath) ... + +# 464| kpGet_mayB_x +#-----| -> (KeyPath) ... + +# 464| kpGet_mayB_x +#-----| -> (KeyPath) ... + +# 464| kpGet_mayB_x +#-----| -> (KeyPath) ... + # 464| kpGet_mayB_x #-----| -> (KeyPath) ... diff --git a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected index 1bcceb82f22..e07930e7ee4 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/DataFlow.expected @@ -12,23 +12,23 @@ edges | test.swift:29:26:29:29 | y : | test.swift:31:15:31:15 | y | | test.swift:35:12:35:19 | call to source() : | test.swift:39:15:39:29 | call to callee_source() | | test.swift:43:19:43:26 | call to source() : | test.swift:50:15:50:15 | t | -| test.swift:53:1:56:1 | arg[return] : | test.swift:61:5:61:24 | arg : | +| test.swift:53:1:56:1 | arg[return] : | test.swift:61:17:61:23 | arg: &... : | | test.swift:54:11:54:18 | call to source() : | test.swift:53:1:56:1 | arg[return] : | -| test.swift:61:5:61:24 | arg : | test.swift:62:15:62:15 | x | +| test.swift:61:17:61:23 | arg: &... : | test.swift:62:15:62:15 | x | | test.swift:65:16:65:28 | WriteDef : | test.swift:65:1:70:1 | arg2[return] : | | test.swift:65:16:65:28 | arg1 : | test.swift:65:1:70:1 | arg2[return] : | | test.swift:73:18:73:25 | call to source() : | test.swift:75:21:75:22 | &... : | -| test.swift:75:5:75:33 | arg2 : | test.swift:77:15:77:15 | y | | test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | WriteDef : | | test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | arg1 : | -| test.swift:75:21:75:22 | &... : | test.swift:75:5:75:33 | arg2 : | -| test.swift:80:1:82:1 | arg[return] : | test.swift:97:9:97:41 | arg : | +| test.swift:75:21:75:22 | &... : | test.swift:75:25:75:32 | arg2: &... : | +| test.swift:75:25:75:32 | arg2: &... : | test.swift:77:15:77:15 | y | +| test.swift:80:1:82:1 | arg[return] : | test.swift:97:34:97:40 | arg: &... : | | test.swift:81:11:81:18 | call to source() : | test.swift:80:1:82:1 | arg[return] : | -| test.swift:84:1:91:1 | arg[return] : | test.swift:104:9:104:54 | arg : | +| test.swift:84:1:91:1 | arg[return] : | test.swift:104:35:104:41 | arg: &... : | | test.swift:86:15:86:22 | call to source() : | test.swift:84:1:91:1 | arg[return] : | | test.swift:89:15:89:22 | call to source() : | test.swift:84:1:91:1 | arg[return] : | -| test.swift:97:9:97:41 | arg : | test.swift:98:19:98:19 | x | -| test.swift:104:9:104:54 | arg : | test.swift:105:19:105:19 | x | +| test.swift:97:34:97:40 | arg: &... : | test.swift:98:19:98:19 | x | +| test.swift:104:35:104:41 | arg: &... : | test.swift:105:19:105:19 | x | | test.swift:109:9:109:14 | WriteDef : | test.swift:110:12:110:12 | arg : | | test.swift:109:9:109:14 | arg : | test.swift:110:12:110:12 | arg : | | test.swift:113:14:113:19 | WriteDef : | test.swift:114:19:114:19 | arg : | @@ -88,7 +88,7 @@ nodes | test.swift:50:15:50:15 | t | semmle.label | t | | test.swift:53:1:56:1 | arg[return] : | semmle.label | arg[return] : | | test.swift:54:11:54:18 | call to source() : | semmle.label | call to source() : | -| test.swift:61:5:61:24 | arg : | semmle.label | arg : | +| test.swift:61:17:61:23 | arg: &... : | semmle.label | arg: &... : | | test.swift:62:15:62:15 | x | semmle.label | x | | test.swift:65:1:70:1 | arg2[return] : | semmle.label | arg2[return] : | | test.swift:65:16:65:28 | WriteDef : | semmle.label | WriteDef : | @@ -96,17 +96,17 @@ nodes | test.swift:65:16:65:28 | arg1 : | semmle.label | WriteDef : | | test.swift:65:16:65:28 | arg1 : | semmle.label | arg1 : | | test.swift:73:18:73:25 | call to source() : | semmle.label | call to source() : | -| test.swift:75:5:75:33 | arg2 : | semmle.label | arg2 : | | test.swift:75:21:75:22 | &... : | semmle.label | &... : | +| test.swift:75:25:75:32 | arg2: &... : | semmle.label | arg2: &... : | | test.swift:77:15:77:15 | y | semmle.label | y | | test.swift:80:1:82:1 | arg[return] : | semmle.label | arg[return] : | | test.swift:81:11:81:18 | call to source() : | semmle.label | call to source() : | | test.swift:84:1:91:1 | arg[return] : | semmle.label | arg[return] : | | test.swift:86:15:86:22 | call to source() : | semmle.label | call to source() : | | test.swift:89:15:89:22 | call to source() : | semmle.label | call to source() : | -| test.swift:97:9:97:41 | arg : | semmle.label | arg : | +| test.swift:97:34:97:40 | arg: &... : | semmle.label | arg: &... : | | test.swift:98:19:98:19 | x | semmle.label | x | -| test.swift:104:9:104:54 | arg : | semmle.label | arg : | +| test.swift:104:35:104:41 | arg: &... : | semmle.label | arg: &... : | | test.swift:105:19:105:19 | x | semmle.label | x | | test.swift:109:9:109:14 | WriteDef : | semmle.label | WriteDef : | | test.swift:109:9:109:14 | WriteDef : | semmle.label | arg : | @@ -155,8 +155,8 @@ nodes | test.swift:157:16:157:23 | call to source() : | semmle.label | call to source() : | | test.swift:159:16:159:29 | call to ... : | semmle.label | call to ... : | subpaths -| test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | WriteDef : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:5:75:33 | arg2 : | -| test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | arg1 : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:5:75:33 | arg2 : | +| test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | WriteDef : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:25:75:32 | arg2: &... : | +| test.swift:75:21:75:22 | &... : | test.swift:65:16:65:28 | arg1 : | test.swift:65:1:70:1 | arg2[return] : | test.swift:75:25:75:32 | arg2: &... : | | test.swift:114:19:114:19 | arg : | test.swift:109:9:109:14 | WriteDef : | test.swift:110:12:110:12 | arg : | test.swift:114:12:114:22 | call to ... : | | test.swift:114:19:114:19 | arg : | test.swift:109:9:109:14 | arg : | test.swift:110:12:110:12 | arg : | test.swift:114:12:114:22 | call to ... : | | test.swift:114:19:114:19 | arg : | test.swift:123:10:123:13 | WriteDef : | test.swift:124:16:124:16 | i : | test.swift:114:12:114:22 | call to ... : | diff --git a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected index 54423a6d8d6..bceacff548e 100644 --- a/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected +++ b/swift/ql/test/library-tests/dataflow/dataflow/LocalFlow.expected @@ -29,7 +29,7 @@ | test.swift:59:18:59:18 | 0 | test.swift:59:9:59:12 | WriteDef | | test.swift:60:15:60:15 | x | test.swift:61:23:61:23 | x | | test.swift:61:5:61:24 | WriteDef | test.swift:62:15:62:15 | x | -| test.swift:61:5:61:24 | arg | test.swift:61:5:61:24 | WriteDef | +| test.swift:61:17:61:23 | arg: &... | test.swift:61:5:61:24 | WriteDef | | test.swift:61:23:61:23 | x | test.swift:61:22:61:23 | &... | | test.swift:65:16:65:28 | WriteDef | test.swift:66:21:66:21 | arg1 | | test.swift:65:16:65:28 | arg1 | test.swift:66:21:66:21 | arg1 | @@ -47,9 +47,9 @@ | test.swift:74:18:74:18 | 0 | test.swift:74:9:74:12 | WriteDef | | test.swift:75:5:75:33 | WriteDef | test.swift:76:15:76:15 | x | | test.swift:75:5:75:33 | WriteDef | test.swift:77:15:77:15 | y | -| test.swift:75:5:75:33 | arg1 | test.swift:75:5:75:33 | WriteDef | -| test.swift:75:5:75:33 | arg2 | test.swift:75:5:75:33 | WriteDef | +| test.swift:75:15:75:22 | arg1: &... | test.swift:75:5:75:33 | WriteDef | | test.swift:75:22:75:22 | x | test.swift:75:21:75:22 | &... | +| test.swift:75:25:75:32 | arg2: &... | test.swift:75:5:75:33 | WriteDef | | test.swift:75:32:75:32 | y | test.swift:75:31:75:32 | &... | | test.swift:81:5:81:18 | WriteDef | test.swift:80:1:82:1 | arg[return] | | test.swift:81:11:81:18 | call to source() | test.swift:81:5:81:18 | WriteDef | @@ -66,13 +66,13 @@ | test.swift:95:22:95:22 | 0 | test.swift:95:13:95:16 | WriteDef | | test.swift:96:19:96:19 | x | test.swift:97:40:97:40 | x | | test.swift:97:9:97:41 | WriteDef | test.swift:98:19:98:19 | x | -| test.swift:97:9:97:41 | arg | test.swift:97:9:97:41 | WriteDef | +| test.swift:97:34:97:40 | arg: &... | test.swift:97:9:97:41 | WriteDef | | test.swift:97:40:97:40 | x | test.swift:97:39:97:40 | &... | | test.swift:102:13:102:16 | WriteDef | test.swift:103:19:103:19 | x | | test.swift:102:22:102:22 | 0 | test.swift:102:13:102:16 | WriteDef | | test.swift:103:19:103:19 | x | test.swift:104:41:104:41 | x | | test.swift:104:9:104:54 | WriteDef | test.swift:105:19:105:19 | x | -| test.swift:104:9:104:54 | arg | test.swift:104:9:104:54 | WriteDef | +| test.swift:104:35:104:41 | arg: &... | test.swift:104:9:104:54 | WriteDef | | test.swift:104:41:104:41 | x | test.swift:104:40:104:41 | &... | | test.swift:109:9:109:14 | WriteDef | test.swift:110:12:110:12 | arg | | test.swift:109:9:109:14 | arg | test.swift:110:12:110:12 | arg | diff --git a/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected new file mode 100644 index 00000000000..6796c2e8c40 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.expected @@ -0,0 +1,121 @@ +| file://:0:0:0:0 | Phi | test.swift:7:14:7:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:9:14:9:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:11:14:11:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:14:14:14:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:16:14:16:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:18:14:18:14 | $interpolation | +| file://:0:0:0:0 | Phi | test.swift:21:14:21:14 | $interpolation | +| test.swift:5:7:5:7 | WriteDef | test.swift:7:16:7:16 | x | +| test.swift:5:11:5:18 | call to source() | test.swift:5:7:5:7 | WriteDef | +| test.swift:7:13:7:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:7:14:7:14 | $interpolation | test.swift:7:14:7:14 | &... | +| test.swift:7:14:7:14 | : &... | test.swift:7:14:7:14 | WriteDef | +| test.swift:7:14:7:14 | WriteDef | test.swift:7:15:7:15 | $interpolation | +| test.swift:7:15:7:15 | $interpolation | test.swift:7:15:7:15 | &... | +| test.swift:7:15:7:15 | : &... | test.swift:7:15:7:15 | WriteDef | +| test.swift:7:15:7:15 | WriteDef | test.swift:7:18:7:18 | $interpolation | +| test.swift:7:16:7:16 | x | test.swift:9:16:9:16 | x | +| test.swift:7:18:7:18 | $interpolation | test.swift:7:18:7:18 | &... | +| test.swift:7:18:7:18 | : &... | test.swift:7:18:7:18 | WriteDef | +| test.swift:7:18:7:18 | WriteDef | test.swift:7:13:7:13 | TapExpr | +| test.swift:9:13:9:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:9:14:9:14 | $interpolation | test.swift:9:14:9:14 | &... | +| test.swift:9:14:9:14 | : &... | test.swift:9:14:9:14 | WriteDef | +| test.swift:9:14:9:14 | WriteDef | test.swift:9:15:9:15 | $interpolation | +| test.swift:9:15:9:15 | $interpolation | test.swift:9:15:9:15 | &... | +| test.swift:9:15:9:15 | : &... | test.swift:9:15:9:15 | WriteDef | +| test.swift:9:15:9:15 | WriteDef | test.swift:9:18:9:18 | $interpolation | +| test.swift:9:16:9:16 | x | test.swift:9:21:9:21 | x | +| test.swift:9:18:9:18 | $interpolation | test.swift:9:18:9:18 | &... | +| test.swift:9:18:9:18 | : &... | test.swift:9:18:9:18 | WriteDef | +| test.swift:9:18:9:18 | WriteDef | test.swift:9:20:9:20 | $interpolation | +| test.swift:9:20:9:20 | $interpolation | test.swift:9:20:9:20 | &... | +| test.swift:9:20:9:20 | : &... | test.swift:9:20:9:20 | WriteDef | +| test.swift:9:20:9:20 | WriteDef | test.swift:9:23:9:23 | $interpolation | +| test.swift:9:21:9:21 | x | test.swift:11:16:11:16 | x | +| test.swift:9:23:9:23 | $interpolation | test.swift:9:23:9:23 | &... | +| test.swift:9:23:9:23 | : &... | test.swift:9:23:9:23 | WriteDef | +| test.swift:9:23:9:23 | WriteDef | test.swift:9:13:9:13 | TapExpr | +| test.swift:11:13:11:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:11:14:11:14 | $interpolation | test.swift:11:14:11:14 | &... | +| test.swift:11:14:11:14 | : &... | test.swift:11:14:11:14 | WriteDef | +| test.swift:11:14:11:14 | WriteDef | test.swift:11:15:11:15 | $interpolation | +| test.swift:11:15:11:15 | $interpolation | test.swift:11:15:11:15 | &... | +| test.swift:11:15:11:15 | : &... | test.swift:11:15:11:15 | WriteDef | +| test.swift:11:15:11:15 | WriteDef | test.swift:11:18:11:18 | $interpolation | +| test.swift:11:16:11:16 | x | test.swift:11:26:11:26 | x | +| test.swift:11:18:11:18 | $interpolation | test.swift:11:18:11:18 | &... | +| test.swift:11:18:11:18 | : &... | test.swift:11:18:11:18 | WriteDef | +| test.swift:11:18:11:18 | WriteDef | test.swift:11:20:11:20 | $interpolation | +| test.swift:11:20:11:20 | $interpolation | test.swift:11:20:11:20 | &... | +| test.swift:11:20:11:20 | : &... | test.swift:11:20:11:20 | WriteDef | +| test.swift:11:20:11:20 | WriteDef | test.swift:11:23:11:23 | $interpolation | +| test.swift:11:23:11:23 | $interpolation | test.swift:11:23:11:23 | &... | +| test.swift:11:23:11:23 | : &... | test.swift:11:23:11:23 | WriteDef | +| test.swift:11:23:11:23 | WriteDef | test.swift:11:25:11:25 | $interpolation | +| test.swift:11:25:11:25 | $interpolation | test.swift:11:25:11:25 | &... | +| test.swift:11:25:11:25 | : &... | test.swift:11:25:11:25 | WriteDef | +| test.swift:11:25:11:25 | WriteDef | test.swift:11:28:11:28 | $interpolation | +| test.swift:11:26:11:26 | x | test.swift:16:16:16:16 | x | +| test.swift:11:28:11:28 | $interpolation | test.swift:11:28:11:28 | &... | +| test.swift:11:28:11:28 | : &... | test.swift:11:28:11:28 | WriteDef | +| test.swift:11:28:11:28 | WriteDef | test.swift:11:13:11:13 | TapExpr | +| test.swift:13:7:13:7 | WriteDef | test.swift:14:16:14:16 | y | +| test.swift:13:11:13:11 | 42 | test.swift:13:7:13:7 | WriteDef | +| test.swift:14:13:14:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:14:14:14:14 | $interpolation | test.swift:14:14:14:14 | &... | +| test.swift:14:14:14:14 | : &... | test.swift:14:14:14:14 | WriteDef | +| test.swift:14:14:14:14 | WriteDef | test.swift:14:15:14:15 | $interpolation | +| test.swift:14:15:14:15 | $interpolation | test.swift:14:15:14:15 | &... | +| test.swift:14:15:14:15 | : &... | test.swift:14:15:14:15 | WriteDef | +| test.swift:14:15:14:15 | WriteDef | test.swift:14:18:14:18 | $interpolation | +| test.swift:14:16:14:16 | y | test.swift:16:27:16:27 | y | +| test.swift:14:18:14:18 | $interpolation | test.swift:14:18:14:18 | &... | +| test.swift:14:18:14:18 | : &... | test.swift:14:18:14:18 | WriteDef | +| test.swift:14:18:14:18 | WriteDef | test.swift:14:13:14:13 | TapExpr | +| test.swift:16:13:16:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:16:14:16:14 | $interpolation | test.swift:16:14:16:14 | &... | +| test.swift:16:14:16:14 | : &... | test.swift:16:14:16:14 | WriteDef | +| test.swift:16:14:16:14 | WriteDef | test.swift:16:15:16:15 | $interpolation | +| test.swift:16:15:16:15 | $interpolation | test.swift:16:15:16:15 | &... | +| test.swift:16:15:16:15 | : &... | test.swift:16:15:16:15 | WriteDef | +| test.swift:16:15:16:15 | WriteDef | test.swift:16:18:16:18 | $interpolation | +| test.swift:16:16:16:16 | x | test.swift:18:27:18:27 | x | +| test.swift:16:18:16:18 | $interpolation | test.swift:16:18:16:18 | &... | +| test.swift:16:18:16:18 | : &... | test.swift:16:18:16:18 | WriteDef | +| test.swift:16:18:16:18 | WriteDef | test.swift:16:26:16:26 | $interpolation | +| test.swift:16:26:16:26 | $interpolation | test.swift:16:26:16:26 | &... | +| test.swift:16:26:16:26 | : &... | test.swift:16:26:16:26 | WriteDef | +| test.swift:16:26:16:26 | WriteDef | test.swift:16:29:16:29 | $interpolation | +| test.swift:16:27:16:27 | y | test.swift:18:16:18:16 | y | +| test.swift:16:29:16:29 | $interpolation | test.swift:16:29:16:29 | &... | +| test.swift:16:29:16:29 | : &... | test.swift:16:29:16:29 | WriteDef | +| test.swift:16:29:16:29 | WriteDef | test.swift:16:13:16:13 | TapExpr | +| test.swift:18:13:18:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:18:14:18:14 | $interpolation | test.swift:18:14:18:14 | &... | +| test.swift:18:14:18:14 | : &... | test.swift:18:14:18:14 | WriteDef | +| test.swift:18:14:18:14 | WriteDef | test.swift:18:15:18:15 | $interpolation | +| test.swift:18:15:18:15 | $interpolation | test.swift:18:15:18:15 | &... | +| test.swift:18:15:18:15 | : &... | test.swift:18:15:18:15 | WriteDef | +| test.swift:18:15:18:15 | WriteDef | test.swift:18:18:18:18 | $interpolation | +| test.swift:18:18:18:18 | $interpolation | test.swift:18:18:18:18 | &... | +| test.swift:18:18:18:18 | : &... | test.swift:18:18:18:18 | WriteDef | +| test.swift:18:18:18:18 | WriteDef | test.swift:18:26:18:26 | $interpolation | +| test.swift:18:26:18:26 | $interpolation | test.swift:18:26:18:26 | &... | +| test.swift:18:26:18:26 | : &... | test.swift:18:26:18:26 | WriteDef | +| test.swift:18:26:18:26 | WriteDef | test.swift:18:29:18:29 | $interpolation | +| test.swift:18:29:18:29 | $interpolation | test.swift:18:29:18:29 | &... | +| test.swift:18:29:18:29 | : &... | test.swift:18:29:18:29 | WriteDef | +| test.swift:18:29:18:29 | WriteDef | test.swift:18:13:18:13 | TapExpr | +| test.swift:20:3:20:7 | WriteDef | test.swift:21:16:21:16 | x | +| test.swift:20:7:20:7 | 0 | test.swift:20:3:20:7 | WriteDef | +| test.swift:21:13:21:13 | WriteDef | file://:0:0:0:0 | Phi | +| test.swift:21:14:21:14 | $interpolation | test.swift:21:14:21:14 | &... | +| test.swift:21:14:21:14 | : &... | test.swift:21:14:21:14 | WriteDef | +| test.swift:21:14:21:14 | WriteDef | test.swift:21:15:21:15 | $interpolation | +| test.swift:21:15:21:15 | $interpolation | test.swift:21:15:21:15 | &... | +| test.swift:21:15:21:15 | : &... | test.swift:21:15:21:15 | WriteDef | +| test.swift:21:15:21:15 | WriteDef | test.swift:21:18:21:18 | $interpolation | +| test.swift:21:18:21:18 | $interpolation | test.swift:21:18:21:18 | &... | +| test.swift:21:18:21:18 | : &... | test.swift:21:18:21:18 | WriteDef | +| test.swift:21:18:21:18 | WriteDef | test.swift:21:13:21:13 | TapExpr | diff --git a/swift/ql/test/library-tests/dataflow/taint/LocalTaint.ql b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.ql new file mode 100644 index 00000000000..04a547b939d --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/taint/LocalTaint.ql @@ -0,0 +1,6 @@ +import swift +import codeql.swift.dataflow.DataFlow + +from DataFlow::Node pred, DataFlow::Node succ +where DataFlow::localFlowStep(pred, succ) +select pred, succ diff --git a/swift/ql/test/library-tests/dataflow/taint/Taint.expected b/swift/ql/test/library-tests/dataflow/taint/Taint.expected new file mode 100644 index 00000000000..fea536d78c0 --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/taint/Taint.expected @@ -0,0 +1,20 @@ +edges +| test.swift:5:11:5:18 | call to source() : | test.swift:7:13:7:13 | "..." | +| test.swift:5:11:5:18 | call to source() : | test.swift:9:13:9:13 | "..." | +| test.swift:5:11:5:18 | call to source() : | test.swift:11:13:11:13 | "..." | +| test.swift:5:11:5:18 | call to source() : | test.swift:16:13:16:13 | "..." | +| test.swift:5:11:5:18 | call to source() : | test.swift:18:13:18:13 | "..." | +nodes +| test.swift:5:11:5:18 | call to source() : | semmle.label | call to source() : | +| test.swift:7:13:7:13 | "..." | semmle.label | "..." | +| test.swift:9:13:9:13 | "..." | semmle.label | "..." | +| test.swift:11:13:11:13 | "..." | semmle.label | "..." | +| test.swift:16:13:16:13 | "..." | semmle.label | "..." | +| test.swift:18:13:18:13 | "..." | semmle.label | "..." | +subpaths +#select +| test.swift:7:13:7:13 | "..." | test.swift:5:11:5:18 | call to source() : | test.swift:7:13:7:13 | "..." | result | +| test.swift:9:13:9:13 | "..." | test.swift:5:11:5:18 | call to source() : | test.swift:9:13:9:13 | "..." | result | +| test.swift:11:13:11:13 | "..." | test.swift:5:11:5:18 | call to source() : | test.swift:11:13:11:13 | "..." | result | +| test.swift:16:13:16:13 | "..." | test.swift:5:11:5:18 | call to source() : | test.swift:16:13:16:13 | "..." | result | +| test.swift:18:13:18:13 | "..." | test.swift:5:11:5:18 | call to source() : | test.swift:18:13:18:13 | "..." | result | diff --git a/swift/ql/test/library-tests/dataflow/taint/Taint.ql b/swift/ql/test/library-tests/dataflow/taint/Taint.ql new file mode 100644 index 00000000000..35836c4cffc --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/taint/Taint.ql @@ -0,0 +1,29 @@ +/** + * @kind path-problem + */ + +import swift +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.DataFlow::DataFlow +import PathGraph + +class TestConfiguration extends TaintTracking::Configuration { + TestConfiguration() { this = "TestConfiguration" } + + override predicate isSource(Node src) { + src.asExpr().(CallExpr).getStaticTarget().getName() = "source()" + } + + override predicate isSink(Node sink) { + exists(CallExpr sinkCall | + sinkCall.getStaticTarget().getName() = "sink(arg:)" and + sinkCall.getAnArgument().getExpr() = sink.asExpr() + ) + } + + override int explorationLimit() { result = 100 } +} + +from PathNode src, PathNode sink, TestConfiguration test +where test.hasFlowPath(src, sink) +select sink, src, sink, "result" diff --git a/swift/ql/test/library-tests/dataflow/taint/test.swift b/swift/ql/test/library-tests/dataflow/taint/test.swift new file mode 100644 index 00000000000..36ff201788b --- /dev/null +++ b/swift/ql/test/library-tests/dataflow/taint/test.swift @@ -0,0 +1,22 @@ +func source() -> Int { return 0; } +func sink(arg: String) {} + +func taintThroughInterpolatedStrings() { + var x = source() + + sink(arg: "\(x)") // tainted + + sink(arg: "\(x) \(x)") // tainted + + sink(arg: "\(x) \(0) \(x)") // tainted + + var y = 42 + sink(arg: "\(y)") // clean + + sink(arg: "\(x) hello \(y)") // tainted + + sink(arg: "\(y) world \(x)") // tainted + + x = 0 + sink(arg: "\(x)") // clean +} \ No newline at end of file diff --git a/swift/ql/test/library-tests/parent/parent.expected b/swift/ql/test/library-tests/parent/parent.expected index ab98c789f82..8ebbf1dd372 100644 --- a/swift/ql/test/library-tests/parent/parent.expected +++ b/swift/ql/test/library-tests/parent/parent.expected @@ -22,13 +22,13 @@ | declarations.swift:3:7:3:7 | yield ... | YieldStmt | file://:0:0:0:0 | &... | InOutExpr | | declarations.swift:3:7:3:7 | { ... } | BraceStmt | declarations.swift:3:7:3:7 | yield ... | YieldStmt | | declarations.swift:3:7:3:14 | ... as ... | TypedPattern | declarations.swift:3:7:3:7 | next | NamedPattern | -| declarations.swift:3:7:3:14 | ... as ... | TypedPattern | declarations.swift:3:14:3:14 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:3:7:3:14 | ... as ... | TypedPattern | declarations.swift:3:14:3:14 | Int | TypeRepr | | declarations.swift:4:5:4:24 | get | AccessorDecl | declarations.swift:4:9:4:24 | { ... } | BraceStmt | | declarations.swift:4:9:4:24 | { ... } | BraceStmt | declarations.swift:4:11:4:22 | return ... | ReturnStmt | | declarations.swift:4:11:4:22 | return ... | ReturnStmt | declarations.swift:4:18:4:22 | ... call to +(_:_:) ... | BinaryExpr | | declarations.swift:4:18:4:18 | .x | MemberRefExpr | declarations.swift:4:18:4:18 | self | DeclRefExpr | | declarations.swift:4:18:4:22 | ... call to +(_:_:) ... | BinaryExpr | declarations.swift:4:20:4:20 | call to +(_:_:) | DotSyntaxCallExpr | -| declarations.swift:4:20:4:20 | Int.Type | TypeExpr | declarations.swift:4:20:4:20 | FixedTypeRepr | FixedTypeRepr | +| declarations.swift:4:20:4:20 | Int.Type | TypeExpr | declarations.swift:4:20:4:20 | Int | TypeRepr | | declarations.swift:4:20:4:20 | call to +(_:_:) | DotSyntaxCallExpr | declarations.swift:4:20:4:20 | +(_:_:) | DeclRefExpr | | declarations.swift:5:5:5:38 | set | AccessorDecl | declarations.swift:5:9:5:9 | newValue | ParamDecl | | declarations.swift:5:5:5:38 | set | AccessorDecl | declarations.swift:5:19:5:38 | { ... } | BraceStmt | @@ -37,7 +37,7 @@ | declarations.swift:5:21:5:36 | ... = ... | AssignExpr | declarations.swift:5:21:5:21 | .x | MemberRefExpr | | declarations.swift:5:21:5:36 | ... = ... | AssignExpr | declarations.swift:5:25:5:36 | ... call to -(_:_:) ... | BinaryExpr | | declarations.swift:5:25:5:36 | ... call to -(_:_:) ... | BinaryExpr | declarations.swift:5:34:5:34 | call to -(_:_:) | DotSyntaxCallExpr | -| declarations.swift:5:34:5:34 | Int.Type | TypeExpr | declarations.swift:5:34:5:34 | FixedTypeRepr | FixedTypeRepr | +| declarations.swift:5:34:5:34 | Int.Type | TypeExpr | declarations.swift:5:34:5:34 | Int | TypeRepr | | declarations.swift:5:34:5:34 | call to -(_:_:) | DotSyntaxCallExpr | declarations.swift:5:34:5:34 | -(_:_:) | DeclRefExpr | | declarations.swift:9:7:9:7 | deinit | DestructorDecl | declarations.swift:9:7:9:7 | { ... } | BraceStmt | | declarations.swift:9:7:9:7 | init | ConstructorDecl | declarations.swift:9:7:9:7 | { ... } | BraceStmt | @@ -56,7 +56,7 @@ | declarations.swift:9:17:9:17 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | declarations.swift:9:17:9:17 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:9:17:9:21 | ... as ... | TypedPattern | declarations.swift:9:17:9:17 | x | NamedPattern | -| declarations.swift:9:17:9:21 | ... as ... | TypedPattern | declarations.swift:9:21:9:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:9:17:9:21 | ... as ... | TypedPattern | declarations.swift:9:21:9:21 | Double | TypeRepr | | declarations.swift:12:5:12:18 | case ... | EnumCaseDecl | declarations.swift:12:10:12:10 | value1 | EnumElementDecl | | declarations.swift:12:5:12:18 | case ... | EnumCaseDecl | declarations.swift:12:18:12:18 | value2 | EnumElementDecl | | declarations.swift:13:5:13:26 | case ... | EnumCaseDecl | declarations.swift:13:10:13:10 | value3 | EnumElementDecl | @@ -75,12 +75,12 @@ | declarations.swift:23:9:23:9 | mustBeSettable | ConcreteVarDecl | declarations.swift:23:31:23:31 | get | AccessorDecl | | declarations.swift:23:9:23:9 | mustBeSettable | ConcreteVarDecl | declarations.swift:23:35:23:35 | set | AccessorDecl | | declarations.swift:23:9:23:25 | ... as ... | TypedPattern | declarations.swift:23:9:23:9 | mustBeSettable | NamedPattern | -| declarations.swift:23:9:23:25 | ... as ... | TypedPattern | declarations.swift:23:25:23:25 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:23:9:23:25 | ... as ... | TypedPattern | declarations.swift:23:25:23:25 | Int | TypeRepr | | declarations.swift:23:35:23:35 | set | AccessorDecl | declarations.swift:23:35:23:35 | newValue | ParamDecl | | declarations.swift:24:5:24:44 | var ... = ... | PatternBindingDecl | declarations.swift:24:9:24:34 | ... as ... | TypedPattern | | declarations.swift:24:9:24:9 | doesNotNeedToBeSettable | ConcreteVarDecl | declarations.swift:24:40:24:40 | get | AccessorDecl | | declarations.swift:24:9:24:34 | ... as ... | TypedPattern | declarations.swift:24:9:24:9 | doesNotNeedToBeSettable | NamedPattern | -| declarations.swift:24:9:24:34 | ... as ... | TypedPattern | declarations.swift:24:34:24:34 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:24:9:24:34 | ... as ... | TypedPattern | declarations.swift:24:34:24:34 | Int | TypeRepr | | declarations.swift:28:1:28:37 | a_function(a_parameter:) | ConcreteFuncDecl | declarations.swift:28:17:28:31 | a_parameter | ParamDecl | | declarations.swift:28:1:28:37 | a_function(a_parameter:) | ConcreteFuncDecl | declarations.swift:28:36:28:37 | { ... } | BraceStmt | | declarations.swift:30:1:30:18 | var ... = ... | PatternBindingDecl | declarations.swift:30:5:30:5 | a_variable | NamedPattern | @@ -93,7 +93,7 @@ | declarations.swift:31:5:31:5 | a_property | ConcreteVarDecl | declarations.swift:32:3:34:3 | get | AccessorDecl | | declarations.swift:31:5:31:5 | a_property | ConcreteVarDecl | declarations.swift:35:3:35:18 | set | AccessorDecl | | declarations.swift:31:5:31:18 | ... as ... | TypedPattern | declarations.swift:31:5:31:5 | a_property | NamedPattern | -| declarations.swift:31:5:31:18 | ... as ... | TypedPattern | declarations.swift:31:18:31:18 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:31:5:31:18 | ... as ... | TypedPattern | declarations.swift:31:18:31:18 | String | TypeRepr | | declarations.swift:32:3:34:3 | get | AccessorDecl | declarations.swift:32:7:34:3 | { ... } | BraceStmt | | declarations.swift:32:7:34:3 | { ... } | BraceStmt | declarations.swift:33:5:33:12 | return ... | ReturnStmt | | declarations.swift:33:5:33:12 | return ... | ReturnStmt | declarations.swift:33:12:33:12 | here | StringLiteralExpr | @@ -118,7 +118,7 @@ | declarations.swift:41:7:41:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | declarations.swift:41:7:41:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:41:7:41:14 | ... as ... | TypedPattern | declarations.swift:41:7:41:7 | field | NamedPattern | -| declarations.swift:41:7:41:14 | ... as ... | TypedPattern | declarations.swift:41:14:41:14 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:41:7:41:14 | ... as ... | TypedPattern | declarations.swift:41:14:41:14 | Int | TypeRepr | | declarations.swift:42:3:44:3 | init | ConstructorDecl | declarations.swift:42:10:44:3 | { ... } | BraceStmt | | declarations.swift:42:10:44:3 | { ... } | BraceStmt | declarations.swift:43:5:43:13 | ... = ... | AssignExpr | | declarations.swift:42:10:44:3 | { ... } | BraceStmt | declarations.swift:44:3:44:3 | return | ReturnStmt | @@ -139,7 +139,7 @@ | declarations.swift:69:3:73:3 | var ... = ... | PatternBindingDecl | declarations.swift:69:7:69:21 | ... as ... | TypedPattern | | declarations.swift:69:7:69:7 | wrappedValue | ConcreteVarDecl | declarations.swift:70:5:72:5 | get | AccessorDecl | | declarations.swift:69:7:69:21 | ... as ... | TypedPattern | declarations.swift:69:7:69:7 | wrappedValue | NamedPattern | -| declarations.swift:69:7:69:21 | ... as ... | TypedPattern | declarations.swift:69:21:69:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:69:7:69:21 | ... as ... | TypedPattern | declarations.swift:69:21:69:21 | Int | TypeRepr | | declarations.swift:70:5:72:5 | get | AccessorDecl | declarations.swift:70:9:72:5 | { ... } | BraceStmt | | declarations.swift:70:9:72:5 | { ... } | BraceStmt | declarations.swift:71:7:71:14 | return ... | ReturnStmt | | declarations.swift:71:7:71:14 | return ... | ReturnStmt | declarations.swift:71:14:71:14 | 0 | IntegerLiteralExpr | @@ -147,7 +147,7 @@ | declarations.swift:76:19:79:1 | { ... } | BraceStmt | declarations.swift:77:16:77:23 | var ... = ... | PatternBindingDecl | | declarations.swift:76:19:79:1 | { ... } | BraceStmt | declarations.swift:77:20:77:20 | x | ConcreteVarDecl | | declarations.swift:76:19:79:1 | { ... } | BraceStmt | declarations.swift:78:3:78:10 | return ... | ReturnStmt | -| declarations.swift:77:4:77:4 | ZeroWrapper.Type | TypeExpr | declarations.swift:77:4:77:4 | FixedTypeRepr | FixedTypeRepr | +| declarations.swift:77:4:77:4 | ZeroWrapper.Type | TypeExpr | declarations.swift:77:4:77:4 | ZeroWrapper | TypeRepr | | declarations.swift:77:4:77:4 | call to ... | CallExpr | declarations.swift:77:4:77:4 | call to init | ConstructorRefCallExpr | | declarations.swift:77:4:77:4 | call to init | ConstructorRefCallExpr | declarations.swift:77:4:77:4 | init | DeclRefExpr | | declarations.swift:77:16:77:23 | var ... = ... | PatternBindingDecl | declarations.swift:77:20:77:23 | ... as ... | TypedPattern | @@ -156,7 +156,7 @@ | declarations.swift:77:20:77:20 | x | ConcreteVarDecl | declarations.swift:77:20:77:20 | get | AccessorDecl | | declarations.swift:77:20:77:20 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:77:20:77:23 | ... as ... | TypedPattern | declarations.swift:77:20:77:20 | x | NamedPattern | -| declarations.swift:77:20:77:23 | ... as ... | TypedPattern | declarations.swift:77:23:77:23 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:77:20:77:23 | ... as ... | TypedPattern | declarations.swift:77:23:77:23 | Int | TypeRepr | | declarations.swift:78:3:78:10 | return ... | ReturnStmt | declarations.swift:78:10:78:10 | x | DeclRefExpr | | declarations.swift:81:8:81:8 | init | ConstructorDecl | declarations.swift:81:8:81:8 | hasBoth | ParamDecl | | declarations.swift:81:8:81:8 | init | ConstructorDecl | declarations.swift:81:8:81:8 | hasDidSet1 | ParamDecl | @@ -172,7 +172,7 @@ | declarations.swift:82:7:82:7 | yield ... | YieldStmt | file://:0:0:0:0 | &... | InOutExpr | | declarations.swift:82:7:82:7 | { ... } | BraceStmt | declarations.swift:82:7:82:7 | yield ... | YieldStmt | | declarations.swift:82:7:82:22 | ... as ... | TypedPattern | declarations.swift:82:7:82:7 | settableField | NamedPattern | -| declarations.swift:82:7:82:22 | ... as ... | TypedPattern | declarations.swift:82:22:82:22 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:82:7:82:22 | ... as ... | TypedPattern | declarations.swift:82:22:82:22 | Int | TypeRepr | | declarations.swift:83:5:83:11 | set | AccessorDecl | declarations.swift:83:5:83:5 | newValue | ParamDecl | | declarations.swift:83:5:83:11 | set | AccessorDecl | declarations.swift:83:9:83:11 | { ... } | BraceStmt | | declarations.swift:84:5:86:5 | get | AccessorDecl | declarations.swift:84:9:86:5 | { ... } | BraceStmt | @@ -181,14 +181,14 @@ | declarations.swift:91:3:93:3 | var ... = ... | PatternBindingDecl | declarations.swift:91:7:91:23 | ... as ... | TypedPattern | | declarations.swift:91:7:91:7 | readOnlyField1 | ConcreteVarDecl | declarations.swift:91:27:93:3 | get | AccessorDecl | | declarations.swift:91:7:91:23 | ... as ... | TypedPattern | declarations.swift:91:7:91:7 | readOnlyField1 | NamedPattern | -| declarations.swift:91:7:91:23 | ... as ... | TypedPattern | declarations.swift:91:23:91:23 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:91:7:91:23 | ... as ... | TypedPattern | declarations.swift:91:23:91:23 | Int | TypeRepr | | declarations.swift:91:27:93:3 | get | AccessorDecl | declarations.swift:91:27:93:3 | { ... } | BraceStmt | | declarations.swift:91:27:93:3 | { ... } | BraceStmt | declarations.swift:92:5:92:12 | return ... | ReturnStmt | | declarations.swift:92:5:92:12 | return ... | ReturnStmt | declarations.swift:92:12:92:12 | 0 | IntegerLiteralExpr | | declarations.swift:96:3:100:3 | var ... = ... | PatternBindingDecl | declarations.swift:96:7:96:23 | ... as ... | TypedPattern | | declarations.swift:96:7:96:7 | readOnlyField2 | ConcreteVarDecl | declarations.swift:97:5:99:5 | get | AccessorDecl | | declarations.swift:96:7:96:23 | ... as ... | TypedPattern | declarations.swift:96:7:96:7 | readOnlyField2 | NamedPattern | -| declarations.swift:96:7:96:23 | ... as ... | TypedPattern | declarations.swift:96:23:96:23 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:96:7:96:23 | ... as ... | TypedPattern | declarations.swift:96:23:96:23 | Int | TypeRepr | | declarations.swift:97:5:99:5 | get | AccessorDecl | declarations.swift:97:9:99:5 | { ... } | BraceStmt | | declarations.swift:97:9:99:5 | { ... } | BraceStmt | declarations.swift:98:7:98:14 | return ... | ReturnStmt | | declarations.swift:98:7:98:14 | return ... | ReturnStmt | declarations.swift:98:14:98:14 | 0 | IntegerLiteralExpr | @@ -205,7 +205,7 @@ | declarations.swift:102:7:102:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | declarations.swift:102:7:102:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:102:7:102:21 | ... as ... | TypedPattern | declarations.swift:102:7:102:7 | normalField | NamedPattern | -| declarations.swift:102:7:102:21 | ... as ... | TypedPattern | declarations.swift:102:21:102:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:102:7:102:21 | ... as ... | TypedPattern | declarations.swift:102:21:102:21 | Int | TypeRepr | | declarations.swift:104:3:104:3 | (unnamed function decl) | AccessorDecl | declarations.swift:104:3:104:3 | { ... } | BraceStmt | | declarations.swift:104:3:104:3 | (unnamed function decl) | AccessorDecl | file://:0:0:0:0 | x | ParamDecl | | declarations.swift:104:3:104:3 | yield ... | YieldStmt | file://:0:0:0:0 | &... | InOutExpr | @@ -244,7 +244,7 @@ | declarations.swift:115:7:115:7 | { ... } | BraceStmt | file://:0:0:0:0 | call to ... | CallExpr | | declarations.swift:115:7:115:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:115:7:115:21 | ... as ... | TypedPattern | declarations.swift:115:7:115:7 | hasWillSet1 | NamedPattern | -| declarations.swift:115:7:115:21 | ... as ... | TypedPattern | declarations.swift:115:21:115:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:115:7:115:21 | ... as ... | TypedPattern | declarations.swift:115:21:115:21 | Int | TypeRepr | | declarations.swift:116:5:116:25 | willSet | AccessorDecl | declarations.swift:116:13:116:13 | newValue | ParamDecl | | declarations.swift:116:5:116:25 | willSet | AccessorDecl | declarations.swift:116:23:116:25 | { ... } | BraceStmt | | declarations.swift:119:3:121:3 | var ... = ... | PatternBindingDecl | declarations.swift:119:7:119:21 | ... as ... | TypedPattern | @@ -262,7 +262,7 @@ | declarations.swift:119:7:119:7 | { ... } | BraceStmt | file://:0:0:0:0 | call to ... | CallExpr | | declarations.swift:119:7:119:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:119:7:119:21 | ... as ... | TypedPattern | declarations.swift:119:7:119:7 | hasWillSet2 | NamedPattern | -| declarations.swift:119:7:119:21 | ... as ... | TypedPattern | declarations.swift:119:21:119:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:119:7:119:21 | ... as ... | TypedPattern | declarations.swift:119:21:119:21 | Int | TypeRepr | | declarations.swift:120:5:120:15 | willSet | AccessorDecl | declarations.swift:120:5:120:5 | newValue | ParamDecl | | declarations.swift:120:5:120:15 | willSet | AccessorDecl | declarations.swift:120:13:120:15 | { ... } | BraceStmt | | declarations.swift:123:3:125:3 | var ... = ... | PatternBindingDecl | declarations.swift:123:7:123:20 | ... as ... | TypedPattern | @@ -282,7 +282,7 @@ | declarations.swift:123:7:123:7 | { ... } | BraceStmt | file://:0:0:0:0 | tmp | ConcreteVarDecl | | declarations.swift:123:7:123:7 | { ... } | BraceStmt | file://:0:0:0:0 | var ... = ... | PatternBindingDecl | | declarations.swift:123:7:123:20 | ... as ... | TypedPattern | declarations.swift:123:7:123:7 | hasDidSet1 | NamedPattern | -| declarations.swift:123:7:123:20 | ... as ... | TypedPattern | declarations.swift:123:20:123:20 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:123:7:123:20 | ... as ... | TypedPattern | declarations.swift:123:20:123:20 | Int | TypeRepr | | declarations.swift:124:5:124:24 | didSet | AccessorDecl | declarations.swift:124:12:124:12 | oldValue | ParamDecl | | declarations.swift:124:5:124:24 | didSet | AccessorDecl | declarations.swift:124:22:124:24 | { ... } | BraceStmt | | declarations.swift:127:3:129:3 | var ... = ... | PatternBindingDecl | declarations.swift:127:7:127:20 | ... as ... | TypedPattern | @@ -301,7 +301,7 @@ | declarations.swift:127:7:127:7 | { ... } | BraceStmt | file://:0:0:0:0 | call to ... | CallExpr | | declarations.swift:127:7:127:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:127:7:127:20 | ... as ... | TypedPattern | declarations.swift:127:7:127:7 | hasDidSet2 | NamedPattern | -| declarations.swift:127:7:127:20 | ... as ... | TypedPattern | declarations.swift:127:20:127:20 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:127:7:127:20 | ... as ... | TypedPattern | declarations.swift:127:20:127:20 | Int | TypeRepr | | declarations.swift:128:5:128:14 | didSet | AccessorDecl | declarations.swift:128:12:128:14 | { ... } | BraceStmt | | declarations.swift:131:3:135:3 | var ... = ... | PatternBindingDecl | declarations.swift:131:7:131:17 | ... as ... | TypedPattern | | declarations.swift:131:7:131:7 | (unnamed function decl) | AccessorDecl | declarations.swift:131:7:131:7 | { ... } | BraceStmt | @@ -320,7 +320,7 @@ | declarations.swift:131:7:131:7 | { ... } | BraceStmt | file://:0:0:0:0 | call to ... | CallExpr | | declarations.swift:131:7:131:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | declarations.swift:131:7:131:17 | ... as ... | TypedPattern | declarations.swift:131:7:131:7 | hasBoth | NamedPattern | -| declarations.swift:131:7:131:17 | ... as ... | TypedPattern | declarations.swift:131:17:131:17 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| declarations.swift:131:7:131:17 | ... as ... | TypedPattern | declarations.swift:131:17:131:17 | Int | TypeRepr | | declarations.swift:132:5:132:15 | willSet | AccessorDecl | declarations.swift:132:5:132:5 | newValue | ParamDecl | | declarations.swift:132:5:132:15 | willSet | AccessorDecl | declarations.swift:132:13:132:15 | { ... } | BraceStmt | | declarations.swift:134:5:134:14 | didSet | AccessorDecl | declarations.swift:134:12:134:14 | { ... } | BraceStmt | @@ -382,7 +382,7 @@ | expressions.swift:8:1:8:15 | { ... } | BraceStmt | expressions.swift:8:1:8:15 | var ... = ... | PatternBindingDecl | | expressions.swift:8:1:8:15 | { ... } | TopLevelCodeDecl | expressions.swift:8:1:8:15 | { ... } | BraceStmt | | expressions.swift:8:5:8:11 | ... as ... | TypedPattern | expressions.swift:8:5:8:5 | n | NamedPattern | -| expressions.swift:8:5:8:11 | ... as ... | TypedPattern | expressions.swift:8:8:8:11 | ...? | OptionalTypeRepr | +| expressions.swift:8:5:8:11 | ... as ... | TypedPattern | expressions.swift:8:8:8:11 | Int? | TypeRepr | | expressions.swift:11:3:11:8 | case ... | EnumCaseDecl | expressions.swift:11:8:11:8 | failed | EnumElementDecl | | expressions.swift:14:1:18:1 | failure(_:) | ConcreteFuncDecl | expressions.swift:14:14:14:19 | x | ParamDecl | | expressions.swift:14:1:18:1 | failure(_:) | ConcreteFuncDecl | expressions.swift:14:31:18:1 | { ... } | BraceStmt | @@ -390,11 +390,11 @@ | expressions.swift:15:3:17:3 | guard ... else { ... } | GuardStmt | expressions.swift:15:9:15:14 | StmtCondition | StmtCondition | | expressions.swift:15:3:17:3 | guard ... else { ... } | GuardStmt | expressions.swift:15:21:17:3 | { ... } | BraceStmt | | expressions.swift:15:9:15:14 | ... call to !=(_:_:) ... | BinaryExpr | expressions.swift:15:11:15:11 | call to !=(_:_:) | DotSyntaxCallExpr | -| expressions.swift:15:11:15:11 | Int.Type | TypeExpr | expressions.swift:15:11:15:11 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:15:11:15:11 | Int.Type | TypeExpr | expressions.swift:15:11:15:11 | Int | TypeRepr | | expressions.swift:15:11:15:11 | call to !=(_:_:) | DotSyntaxCallExpr | expressions.swift:15:11:15:11 | !=(_:_:) | DeclRefExpr | | expressions.swift:15:21:17:3 | { ... } | BraceStmt | expressions.swift:16:5:16:19 | throw ... | ThrowStmt | | expressions.swift:16:5:16:19 | throw ... | ThrowStmt | expressions.swift:16:11:16:19 | (Error) ... | ErasureExpr | -| expressions.swift:16:11:16:11 | AnError.Type | TypeExpr | expressions.swift:16:11:16:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:16:11:16:11 | AnError.Type | TypeExpr | expressions.swift:16:11:16:11 | AnError | TypeRepr | | expressions.swift:16:11:16:19 | (Error) ... | ErasureExpr | expressions.swift:16:11:16:19 | call to ... | DotSyntaxCallExpr | | expressions.swift:16:11:16:19 | call to ... | DotSyntaxCallExpr | expressions.swift:16:19:16:19 | failed | DeclRefExpr | | expressions.swift:20:1:20:16 | try! ... | ForceTryExpr | expressions.swift:20:6:20:16 | call to failure(_:) | CallExpr | @@ -413,7 +413,7 @@ | expressions.swift:27:1:27:19 | var ... = ... | PatternBindingDecl | expressions.swift:27:13:27:19 | call to ... | CallExpr | | expressions.swift:27:1:27:19 | { ... } | BraceStmt | expressions.swift:27:1:27:19 | var ... = ... | PatternBindingDecl | | expressions.swift:27:1:27:19 | { ... } | TopLevelCodeDecl | expressions.swift:27:1:27:19 | { ... } | BraceStmt | -| expressions.swift:27:13:27:13 | Klass.Type | TypeExpr | expressions.swift:27:13:27:13 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:27:13:27:13 | Klass.Type | TypeExpr | expressions.swift:27:13:27:13 | Klass | TypeRepr | | expressions.swift:27:13:27:13 | call to init | ConstructorRefCallExpr | expressions.swift:27:13:27:13 | init | DeclRefExpr | | expressions.swift:27:13:27:19 | call to ... | CallExpr | expressions.swift:27:13:27:13 | call to init | ConstructorRefCallExpr | | expressions.swift:29:1:29:19 | var ... = ... | PatternBindingDecl | expressions.swift:29:5:29:5 | d | NamedPattern | @@ -467,7 +467,7 @@ | expressions.swift:41:10:43:1 | { ... } | ClosureExpr | expressions.swift:41:21:41:24 | y | ParamDecl | | expressions.swift:42:5:42:16 | return ... | ReturnStmt | expressions.swift:42:12:42:16 | ... call to +(_:_:) ... | BinaryExpr | | expressions.swift:42:12:42:16 | ... call to +(_:_:) ... | BinaryExpr | expressions.swift:42:14:42:14 | call to +(_:_:) | DotSyntaxCallExpr | -| expressions.swift:42:14:42:14 | Int.Type | TypeExpr | expressions.swift:42:14:42:14 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:42:14:42:14 | Int.Type | TypeExpr | expressions.swift:42:14:42:14 | Int | TypeRepr | | expressions.swift:42:14:42:14 | call to +(_:_:) | DotSyntaxCallExpr | expressions.swift:42:14:42:14 | +(_:_:) | DeclRefExpr | | expressions.swift:44:1:46:1 | call to closured(closure:) | CallExpr | expressions.swift:44:1:44:1 | closured(closure:) | DeclRefExpr | | expressions.swift:44:1:46:1 | { ... } | BraceStmt | expressions.swift:44:1:46:1 | call to closured(closure:) | CallExpr | @@ -478,7 +478,7 @@ | expressions.swift:44:10:46:1 | { ... } | ClosureExpr | expressions.swift:44:15:44:15 | y | ParamDecl | | expressions.swift:45:5:45:16 | return ... | ReturnStmt | expressions.swift:45:12:45:16 | ... call to +(_:_:) ... | BinaryExpr | | expressions.swift:45:12:45:16 | ... call to +(_:_:) ... | BinaryExpr | expressions.swift:45:14:45:14 | call to +(_:_:) | DotSyntaxCallExpr | -| expressions.swift:45:14:45:14 | Int.Type | TypeExpr | expressions.swift:45:14:45:14 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:45:14:45:14 | Int.Type | TypeExpr | expressions.swift:45:14:45:14 | Int | TypeRepr | | expressions.swift:45:14:45:14 | call to +(_:_:) | DotSyntaxCallExpr | expressions.swift:45:14:45:14 | +(_:_:) | DeclRefExpr | | expressions.swift:47:1:47:27 | call to closured(closure:) | CallExpr | expressions.swift:47:1:47:1 | closured(closure:) | DeclRefExpr | | expressions.swift:47:1:47:27 | { ... } | BraceStmt | expressions.swift:47:1:47:27 | call to closured(closure:) | CallExpr | @@ -489,7 +489,7 @@ | expressions.swift:47:10:47:27 | { ... } | ClosureExpr | expressions.swift:47:10:47:27 | { ... } | BraceStmt | | expressions.swift:47:12:47:24 | return ... | ReturnStmt | expressions.swift:47:19:47:24 | ... call to +(_:_:) ... | BinaryExpr | | expressions.swift:47:19:47:24 | ... call to +(_:_:) ... | BinaryExpr | expressions.swift:47:22:47:22 | call to +(_:_:) | DotSyntaxCallExpr | -| expressions.swift:47:22:47:22 | Int.Type | TypeExpr | expressions.swift:47:22:47:22 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:47:22:47:22 | Int.Type | TypeExpr | expressions.swift:47:22:47:22 | Int | TypeRepr | | expressions.swift:47:22:47:22 | call to +(_:_:) | DotSyntaxCallExpr | expressions.swift:47:22:47:22 | +(_:_:) | DeclRefExpr | | expressions.swift:48:1:48:20 | call to closured(closure:) | CallExpr | expressions.swift:48:1:48:1 | closured(closure:) | DeclRefExpr | | expressions.swift:48:1:48:20 | { ... } | BraceStmt | expressions.swift:48:1:48:20 | call to closured(closure:) | CallExpr | @@ -500,7 +500,7 @@ | expressions.swift:48:10:48:20 | { ... } | ClosureExpr | expressions.swift:48:10:48:20 | { ... } | BraceStmt | | expressions.swift:48:12:48:17 | ... call to +(_:_:) ... | BinaryExpr | expressions.swift:48:15:48:15 | call to +(_:_:) | DotSyntaxCallExpr | | expressions.swift:48:12:48:17 | return ... | ReturnStmt | expressions.swift:48:12:48:17 | ... call to +(_:_:) ... | BinaryExpr | -| expressions.swift:48:15:48:15 | Int.Type | TypeExpr | expressions.swift:48:15:48:15 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:48:15:48:15 | Int.Type | TypeExpr | expressions.swift:48:15:48:15 | Int | TypeRepr | | expressions.swift:48:15:48:15 | call to +(_:_:) | DotSyntaxCallExpr | expressions.swift:48:15:48:15 | +(_:_:) | DeclRefExpr | | expressions.swift:50:8:50:8 | init | ConstructorDecl | expressions.swift:50:8:50:8 | x | ParamDecl | | expressions.swift:51:3:51:10 | var ... = ... | PatternBindingDecl | expressions.swift:51:7:51:10 | ... as ... | TypedPattern | @@ -508,14 +508,12 @@ | expressions.swift:51:7:51:7 | x | ConcreteVarDecl | expressions.swift:51:7:51:7 | get | AccessorDecl | | expressions.swift:51:7:51:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:51:7:51:10 | ... as ... | TypedPattern | expressions.swift:51:7:51:7 | x | NamedPattern | -| expressions.swift:51:7:51:10 | ... as ... | TypedPattern | expressions.swift:51:10:51:10 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:51:7:51:10 | ... as ... | TypedPattern | expressions.swift:51:10:51:10 | Int | TypeRepr | | expressions.swift:54:1:54:8 | ... = ... | AssignExpr | expressions.swift:54:1:54:1 | _ | DiscardAssignmentExpr | | expressions.swift:54:1:54:8 | ... = ... | AssignExpr | expressions.swift:54:5:54:8 | #keyPath(...) | KeyPathExpr | | expressions.swift:54:1:54:8 | { ... } | BraceStmt | expressions.swift:54:1:54:8 | ... = ... | AssignExpr | | expressions.swift:54:1:54:8 | { ... } | TopLevelCodeDecl | expressions.swift:54:1:54:8 | { ... } | BraceStmt | -| expressions.swift:54:5:54:8 | #keyPath(...) | KeyPathExpr | expressions.swift:54:6:54:8 | ... .x | UnresolvedDotExpr | -| expressions.swift:54:6:54:6 | (no string representation) | TypeExpr | expressions.swift:54:6:54:6 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | -| expressions.swift:54:6:54:8 | ... .x | UnresolvedDotExpr | expressions.swift:54:6:54:6 | (no string representation) | TypeExpr | +| expressions.swift:54:5:54:8 | #keyPath(...) | KeyPathExpr | expressions.swift:54:6:54:6 | S | TypeRepr | | expressions.swift:56:1:57:1 | unsafeFunction(pointer:) | ConcreteFuncDecl | expressions.swift:56:21:56:47 | pointer | ParamDecl | | expressions.swift:56:1:57:1 | unsafeFunction(pointer:) | ConcreteFuncDecl | expressions.swift:56:50:57:1 | { ... } | BraceStmt | | expressions.swift:58:1:58:16 | var ... = ... | PatternBindingDecl | expressions.swift:58:5:58:5 | myNumber | NamedPattern | @@ -545,7 +543,7 @@ | expressions.swift:64:5:66:5 | if ... then { ... } | IfStmt | expressions.swift:64:8:64:12 | StmtCondition | StmtCondition | | expressions.swift:64:5:66:5 | if ... then { ... } | IfStmt | expressions.swift:64:14:66:5 | { ... } | BraceStmt | | expressions.swift:64:8:64:12 | ... call to <(_:_:) ... | BinaryExpr | expressions.swift:64:10:64:10 | call to <(_:_:) | DotSyntaxCallExpr | -| expressions.swift:64:10:64:10 | Int.Type | TypeExpr | expressions.swift:64:10:64:10 | FixedTypeRepr | FixedTypeRepr | +| expressions.swift:64:10:64:10 | Int.Type | TypeExpr | expressions.swift:64:10:64:10 | Int | TypeRepr | | expressions.swift:64:10:64:10 | call to <(_:_:) | DotSyntaxCallExpr | expressions.swift:64:10:64:10 | <(_:_:) | DeclRefExpr | | expressions.swift:64:14:66:5 | { ... } | BraceStmt | expressions.swift:65:7:65:14 | fail | FailStmt | | expressions.swift:70:7:70:7 | deinit | DestructorDecl | expressions.swift:70:7:70:7 | { ... } | BraceStmt | @@ -554,7 +552,7 @@ | expressions.swift:71:7:71:7 | xx | ConcreteVarDecl | expressions.swift:71:7:71:7 | get | AccessorDecl | | expressions.swift:71:7:71:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:71:7:71:11 | ... as ... | TypedPattern | expressions.swift:71:7:71:7 | xx | NamedPattern | -| expressions.swift:71:7:71:11 | ... as ... | TypedPattern | expressions.swift:71:11:71:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:71:7:71:11 | ... as ... | TypedPattern | expressions.swift:71:11:71:11 | Int | TypeRepr | | expressions.swift:72:3:74:3 | init | ConstructorDecl | expressions.swift:72:8:72:11 | x | ParamDecl | | expressions.swift:72:3:74:3 | init | ConstructorDecl | expressions.swift:72:16:74:3 | { ... } | BraceStmt | | expressions.swift:72:16:74:3 | { ... } | BraceStmt | expressions.swift:73:5:73:10 | ... = ... | AssignExpr | @@ -577,7 +575,7 @@ | expressions.swift:83:1:83:23 | var ... = ... | PatternBindingDecl | expressions.swift:83:15:83:23 | call to ... | CallExpr | | expressions.swift:83:1:83:23 | { ... } | BraceStmt | expressions.swift:83:1:83:23 | var ... = ... | PatternBindingDecl | | expressions.swift:83:1:83:23 | { ... } | TopLevelCodeDecl | expressions.swift:83:1:83:23 | { ... } | BraceStmt | -| expressions.swift:83:15:83:15 | Derived.Type | TypeExpr | expressions.swift:83:15:83:15 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:83:15:83:15 | Derived.Type | TypeExpr | expressions.swift:83:15:83:15 | Derived | TypeRepr | | expressions.swift:83:15:83:15 | call to init | ConstructorRefCallExpr | expressions.swift:83:15:83:15 | init | DeclRefExpr | | expressions.swift:83:15:83:23 | call to ... | CallExpr | expressions.swift:83:15:83:15 | call to init | ConstructorRefCallExpr | | expressions.swift:84:1:84:13 | ... = ... | AssignExpr | expressions.swift:84:1:84:1 | _ | DiscardAssignmentExpr | @@ -591,7 +589,7 @@ | expressions.swift:86:1:86:13 | { ... } | BraceStmt | expressions.swift:86:1:86:13 | var ... = ... | PatternBindingDecl | | expressions.swift:86:1:86:13 | { ... } | TopLevelCodeDecl | expressions.swift:86:1:86:13 | { ... } | BraceStmt | | expressions.swift:86:5:86:13 | ... as ... | TypedPattern | expressions.swift:86:5:86:5 | opt | NamedPattern | -| expressions.swift:86:5:86:13 | ... as ... | TypedPattern | expressions.swift:86:10:86:13 | ...? | OptionalTypeRepr | +| expressions.swift:86:5:86:13 | ... as ... | TypedPattern | expressions.swift:86:10:86:13 | Int? | TypeRepr | | expressions.swift:87:1:87:4 | ...! | ForceValueExpr | expressions.swift:87:1:87:1 | opt | DeclRefExpr | | expressions.swift:87:1:87:4 | { ... } | BraceStmt | expressions.swift:87:1:87:4 | ...! | ForceValueExpr | | expressions.swift:87:1:87:4 | { ... } | TopLevelCodeDecl | expressions.swift:87:1:87:4 | { ... } | BraceStmt | @@ -606,15 +604,15 @@ | expressions.swift:92:1:92:55 | var ... = ... | PatternBindingDecl | expressions.swift:92:14:92:55 | call to ... | CallExpr | | expressions.swift:92:1:92:55 | { ... } | BraceStmt | expressions.swift:92:1:92:55 | var ... = ... | PatternBindingDecl | | expressions.swift:92:1:92:55 | { ... } | TopLevelCodeDecl | expressions.swift:92:1:92:55 | { ... } | BraceStmt | -| expressions.swift:92:14:92:14 | Unmanaged.Type | TypeExpr | expressions.swift:92:14:92:14 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:92:14:92:14 | Unmanaged.Type | TypeExpr | expressions.swift:92:14:92:14 | Unmanaged | TypeRepr | | expressions.swift:92:14:92:24 | call to passRetained(_:) | DotSyntaxCallExpr | expressions.swift:92:24:92:24 | passRetained(_:) | DeclRefExpr | | expressions.swift:92:14:92:44 | call to ... | CallExpr | expressions.swift:92:14:92:24 | call to passRetained(_:) | DotSyntaxCallExpr | | expressions.swift:92:14:92:46 | call to toOpaque() | DotSyntaxCallExpr | expressions.swift:92:46:92:46 | toOpaque() | DeclRefExpr | | expressions.swift:92:14:92:55 | call to ... | CallExpr | expressions.swift:92:14:92:46 | call to toOpaque() | DotSyntaxCallExpr | -| expressions.swift:92:37:92:37 | ToPtr.Type | TypeExpr | expressions.swift:92:37:92:37 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:92:37:92:37 | ToPtr.Type | TypeExpr | expressions.swift:92:37:92:37 | ToPtr | TypeRepr | | expressions.swift:92:37:92:37 | call to init | ConstructorRefCallExpr | expressions.swift:92:37:92:37 | init | DeclRefExpr | | expressions.swift:92:37:92:43 | call to ... | CallExpr | expressions.swift:92:37:92:37 | call to init | ConstructorRefCallExpr | -| expressions.swift:93:1:93:16 | Unmanaged.Type | TypeExpr | expressions.swift:93:1:93:16 | ...<...> | GenericIdentTypeRepr | +| expressions.swift:93:1:93:16 | Unmanaged.Type | TypeExpr | expressions.swift:93:1:93:16 | Unmanaged | TypeRepr | | expressions.swift:93:1:93:18 | call to fromOpaque(_:) | DotSyntaxCallExpr | expressions.swift:93:18:93:18 | fromOpaque(_:) | DeclRefExpr | | expressions.swift:93:1:93:35 | call to ... | CallExpr | expressions.swift:93:1:93:18 | call to fromOpaque(_:) | DotSyntaxCallExpr | | expressions.swift:93:1:93:35 | { ... } | BraceStmt | expressions.swift:93:1:93:35 | call to ... | CallExpr | @@ -629,7 +627,7 @@ | expressions.swift:96:7:96:7 | yield ... | YieldStmt | file://:0:0:0:0 | &... | InOutExpr | | expressions.swift:96:7:96:7 | { ... } | BraceStmt | expressions.swift:96:7:96:7 | yield ... | YieldStmt | | expressions.swift:96:7:96:22 | ... as ... | TypedPattern | expressions.swift:96:7:96:7 | settableField | NamedPattern | -| expressions.swift:96:7:96:22 | ... as ... | TypedPattern | expressions.swift:96:22:96:22 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:96:7:96:22 | ... as ... | TypedPattern | expressions.swift:96:22:96:22 | Int | TypeRepr | | expressions.swift:97:5:97:11 | set | AccessorDecl | expressions.swift:97:5:97:5 | newValue | ParamDecl | | expressions.swift:97:5:97:11 | set | AccessorDecl | expressions.swift:97:9:97:11 | { ... } | BraceStmt | | expressions.swift:98:5:100:5 | get | AccessorDecl | expressions.swift:98:9:100:5 | { ... } | BraceStmt | @@ -638,14 +636,14 @@ | expressions.swift:105:3:107:3 | var ... = ... | PatternBindingDecl | expressions.swift:105:7:105:23 | ... as ... | TypedPattern | | expressions.swift:105:7:105:7 | readOnlyField1 | ConcreteVarDecl | expressions.swift:105:27:107:3 | get | AccessorDecl | | expressions.swift:105:7:105:23 | ... as ... | TypedPattern | expressions.swift:105:7:105:7 | readOnlyField1 | NamedPattern | -| expressions.swift:105:7:105:23 | ... as ... | TypedPattern | expressions.swift:105:23:105:23 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:105:7:105:23 | ... as ... | TypedPattern | expressions.swift:105:23:105:23 | Int | TypeRepr | | expressions.swift:105:27:107:3 | get | AccessorDecl | expressions.swift:105:27:107:3 | { ... } | BraceStmt | | expressions.swift:105:27:107:3 | { ... } | BraceStmt | expressions.swift:106:5:106:12 | return ... | ReturnStmt | | expressions.swift:106:5:106:12 | return ... | ReturnStmt | expressions.swift:106:12:106:12 | 0 | IntegerLiteralExpr | | expressions.swift:110:3:114:3 | var ... = ... | PatternBindingDecl | expressions.swift:110:7:110:23 | ... as ... | TypedPattern | | expressions.swift:110:7:110:7 | readOnlyField2 | ConcreteVarDecl | expressions.swift:111:5:113:5 | get | AccessorDecl | | expressions.swift:110:7:110:23 | ... as ... | TypedPattern | expressions.swift:110:7:110:7 | readOnlyField2 | NamedPattern | -| expressions.swift:110:7:110:23 | ... as ... | TypedPattern | expressions.swift:110:23:110:23 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:110:7:110:23 | ... as ... | TypedPattern | expressions.swift:110:23:110:23 | Int | TypeRepr | | expressions.swift:111:5:113:5 | get | AccessorDecl | expressions.swift:111:9:113:5 | { ... } | BraceStmt | | expressions.swift:111:9:113:5 | { ... } | BraceStmt | expressions.swift:112:7:112:14 | return ... | ReturnStmt | | expressions.swift:112:7:112:14 | return ... | ReturnStmt | expressions.swift:112:14:112:14 | 0 | IntegerLiteralExpr | @@ -662,7 +660,7 @@ | expressions.swift:116:7:116:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | expressions.swift:116:7:116:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:116:7:116:21 | ... as ... | TypedPattern | expressions.swift:116:7:116:7 | normalField | NamedPattern | -| expressions.swift:116:7:116:21 | ... as ... | TypedPattern | expressions.swift:116:21:116:21 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:116:7:116:21 | ... as ... | TypedPattern | expressions.swift:116:21:116:21 | Int | TypeRepr | | expressions.swift:118:3:118:3 | (unnamed function decl) | AccessorDecl | expressions.swift:118:3:118:3 | { ... } | BraceStmt | | expressions.swift:118:3:118:3 | (unnamed function decl) | AccessorDecl | file://:0:0:0:0 | x | ParamDecl | | expressions.swift:118:3:118:3 | yield ... | YieldStmt | file://:0:0:0:0 | &... | InOutExpr | @@ -743,7 +741,7 @@ | expressions.swift:142:7:142:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | expressions.swift:142:7:142:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:142:7:142:11 | ... as ... | TypedPattern | expressions.swift:142:7:142:7 | x | NamedPattern | -| expressions.swift:142:7:142:11 | ... as ... | TypedPattern | expressions.swift:142:11:142:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:142:7:142:11 | ... as ... | TypedPattern | expressions.swift:142:11:142:11 | Int | TypeRepr | | expressions.swift:145:8:145:8 | init | ConstructorDecl | expressions.swift:145:8:145:8 | b | ParamDecl | | expressions.swift:145:8:145:8 | init | ConstructorDecl | expressions.swift:145:8:145:8 | bs | ParamDecl | | expressions.swift:145:8:145:8 | init | ConstructorDecl | expressions.swift:145:8:145:8 | mayB | ParamDecl | @@ -760,7 +758,7 @@ | expressions.swift:146:7:146:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | expressions.swift:146:7:146:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:146:7:146:11 | ... as ... | TypedPattern | expressions.swift:146:7:146:7 | b | NamedPattern | -| expressions.swift:146:7:146:11 | ... as ... | TypedPattern | expressions.swift:146:11:146:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| expressions.swift:146:7:146:11 | ... as ... | TypedPattern | expressions.swift:146:11:146:11 | B | TypeRepr | | expressions.swift:147:3:147:14 | var ... = ... | PatternBindingDecl | expressions.swift:147:7:147:14 | ... as ... | TypedPattern | | expressions.swift:147:7:147:7 | (unnamed function decl) | AccessorDecl | expressions.swift:147:7:147:7 | { ... } | BraceStmt | | expressions.swift:147:7:147:7 | bs | ConcreteVarDecl | expressions.swift:147:7:147:7 | (unnamed function decl) | AccessorDecl | @@ -774,7 +772,7 @@ | expressions.swift:147:7:147:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | expressions.swift:147:7:147:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:147:7:147:14 | ... as ... | TypedPattern | expressions.swift:147:7:147:7 | bs | NamedPattern | -| expressions.swift:147:7:147:14 | ... as ... | TypedPattern | expressions.swift:147:12:147:14 | [...] | ArrayTypeRepr | +| expressions.swift:147:7:147:14 | ... as ... | TypedPattern | expressions.swift:147:12:147:14 | [B] | TypeRepr | | expressions.swift:148:3:148:15 | var ... = ... | PatternBindingDecl | expressions.swift:148:7:148:15 | ... as ... | TypedPattern | | expressions.swift:148:3:148:15 | var ... = ... | PatternBindingDecl | file://:0:0:0:0 | nil | NilLiteralExpr | | expressions.swift:148:7:148:7 | (unnamed function decl) | AccessorDecl | expressions.swift:148:7:148:7 | { ... } | BraceStmt | @@ -789,7 +787,7 @@ | expressions.swift:148:7:148:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | expressions.swift:148:7:148:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | expressions.swift:148:7:148:15 | ... as ... | TypedPattern | expressions.swift:148:7:148:7 | mayB | NamedPattern | -| expressions.swift:148:7:148:15 | ... as ... | TypedPattern | expressions.swift:148:14:148:15 | ...? | OptionalTypeRepr | +| expressions.swift:148:7:148:15 | ... as ... | TypedPattern | expressions.swift:148:14:148:15 | B? | TypeRepr | | expressions.swift:151:1:155:1 | test(a:keyPathInt:keyPathB:) | ConcreteFuncDecl | expressions.swift:151:11:151:15 | a | ParamDecl | | expressions.swift:151:1:155:1 | test(a:keyPathInt:keyPathB:) | ConcreteFuncDecl | expressions.swift:151:18:151:53 | keyPathInt | ParamDecl | | expressions.swift:151:1:155:1 | test(a:keyPathInt:keyPathB:) | ConcreteFuncDecl | expressions.swift:151:56:151:87 | keyPathB | ParamDecl | @@ -814,9 +812,7 @@ | expressions.swift:154:22:154:41 | \\...[...] | KeyPathApplicationExpr | expressions.swift:154:33:154:33 | keyPathB | DeclRefExpr | | expressions.swift:154:22:154:56 | \\...[...] | KeyPathApplicationExpr | expressions.swift:154:22:154:41 | \\...[...] | KeyPathApplicationExpr | | expressions.swift:154:22:154:56 | \\...[...] | KeyPathApplicationExpr | expressions.swift:154:52:154:55 | #keyPath(...) | KeyPathExpr | -| expressions.swift:154:52:154:55 | #keyPath(...) | KeyPathExpr | expressions.swift:154:53:154:55 | ... .x | UnresolvedDotExpr | -| expressions.swift:154:53:154:53 | (no string representation) | TypeExpr | expressions.swift:154:53:154:53 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | -| expressions.swift:154:53:154:55 | ... .x | UnresolvedDotExpr | expressions.swift:154:53:154:53 | (no string representation) | TypeExpr | +| expressions.swift:154:52:154:55 | #keyPath(...) | KeyPathExpr | expressions.swift:154:53:154:53 | B | TypeRepr | | patterns.swift:1:1:7:1 | basic_patterns() | ConcreteFuncDecl | patterns.swift:1:23:7:1 | { ... } | BraceStmt | | patterns.swift:1:23:7:1 | { ... } | BraceStmt | patterns.swift:2:5:2:18 | var ... = ... | PatternBindingDecl | | patterns.swift:1:23:7:1 | { ... } | BraceStmt | patterns.swift:2:9:2:9 | an_int | ConcreteVarDecl | @@ -833,7 +829,7 @@ | patterns.swift:3:5:3:28 | var ... = ... | PatternBindingDecl | patterns.swift:3:9:3:19 | ... as ... | TypedPattern | | patterns.swift:3:5:3:28 | var ... = ... | PatternBindingDecl | patterns.swift:3:28:3:28 | here | StringLiteralExpr | | patterns.swift:3:9:3:19 | ... as ... | TypedPattern | patterns.swift:3:9:3:9 | a_string | NamedPattern | -| patterns.swift:3:9:3:19 | ... as ... | TypedPattern | patterns.swift:3:19:3:19 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| patterns.swift:3:9:3:19 | ... as ... | TypedPattern | patterns.swift:3:19:3:19 | String | TypeRepr | | patterns.swift:4:5:4:29 | var ... = ... | PatternBindingDecl | patterns.swift:4:9:4:17 | (...) | TuplePattern | | patterns.swift:4:5:4:29 | var ... = ... | PatternBindingDecl | patterns.swift:4:21:4:29 | (...) | TupleExpr | | patterns.swift:4:9:4:17 | (...) | TuplePattern | patterns.swift:4:10:4:10 | x | NamedPattern | @@ -900,8 +896,8 @@ | patterns.swift:24:5:24:19 | var ... = ... | PatternBindingDecl | patterns.swift:24:9:24:12 | ... as ... | TypedPattern | | patterns.swift:24:5:24:19 | var ... = ... | PatternBindingDecl | patterns.swift:24:18:24:19 | call to ... | DotSyntaxCallExpr | | patterns.swift:24:9:24:12 | ... as ... | TypedPattern | patterns.swift:24:9:24:9 | v | NamedPattern | -| patterns.swift:24:9:24:12 | ... as ... | TypedPattern | patterns.swift:24:12:24:12 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | -| patterns.swift:24:18:24:18 | Foo.Type | TypeExpr | patterns.swift:24:18:24:18 | FixedTypeRepr | FixedTypeRepr | +| patterns.swift:24:9:24:12 | ... as ... | TypedPattern | patterns.swift:24:12:24:12 | Foo | TypeRepr | +| patterns.swift:24:18:24:18 | Foo.Type | TypeExpr | patterns.swift:24:18:24:18 | Foo | TypeRepr | | patterns.swift:24:18:24:19 | call to ... | DotSyntaxCallExpr | patterns.swift:24:19:24:19 | bar | DeclRefExpr | | patterns.swift:26:5:29:5 | switch v { ... } | SwitchStmt | patterns.swift:26:12:26:12 | v | DeclRefExpr | | patterns.swift:26:5:29:5 | switch v { ... } | SwitchStmt | patterns.swift:27:5:27:16 | case ... | CaseStmt | @@ -921,7 +917,7 @@ | patterns.swift:31:5:31:19 | var ... = ... | PatternBindingDecl | patterns.swift:31:9:31:15 | ... as ... | TypedPattern | | patterns.swift:31:5:31:19 | var ... = ... | PatternBindingDecl | patterns.swift:31:19:31:19 | nil | NilLiteralExpr | | patterns.swift:31:9:31:15 | ... as ... | TypedPattern | patterns.swift:31:9:31:9 | w | NamedPattern | -| patterns.swift:31:9:31:15 | ... as ... | TypedPattern | patterns.swift:31:12:31:15 | ...? | OptionalTypeRepr | +| patterns.swift:31:9:31:15 | ... as ... | TypedPattern | patterns.swift:31:12:31:15 | Int? | TypeRepr | | patterns.swift:33:5:36:5 | switch w { ... } | SwitchStmt | patterns.swift:33:12:33:12 | w | DeclRefExpr | | patterns.swift:33:5:36:5 | switch w { ... } | SwitchStmt | patterns.swift:34:5:34:18 | case ... | CaseStmt | | patterns.swift:33:5:36:5 | switch w { ... } | SwitchStmt | patterns.swift:35:5:35:13 | case ... | CaseStmt | @@ -938,7 +934,7 @@ | patterns.swift:38:5:38:18 | var ... = ... | PatternBindingDecl | patterns.swift:38:9:38:12 | ... as ... | TypedPattern | | patterns.swift:38:5:38:18 | var ... = ... | PatternBindingDecl | patterns.swift:38:18:38:18 | (Any) ... | ErasureExpr | | patterns.swift:38:9:38:12 | ... as ... | TypedPattern | patterns.swift:38:9:38:9 | a | NamedPattern | -| patterns.swift:38:9:38:12 | ... as ... | TypedPattern | patterns.swift:38:12:38:12 | CompositionTypeRepr | CompositionTypeRepr | +| patterns.swift:38:9:38:12 | ... as ... | TypedPattern | patterns.swift:38:12:38:12 | Any | TypeRepr | | patterns.swift:38:18:38:18 | (Any) ... | ErasureExpr | patterns.swift:38:18:38:18 | any | StringLiteralExpr | | patterns.swift:40:5:44:5 | switch a { ... } | SwitchStmt | patterns.swift:40:12:40:12 | a | DeclRefExpr | | patterns.swift:40:5:44:5 | switch a { ... } | SwitchStmt | patterns.swift:41:5:41:18 | case ... | CaseStmt | @@ -947,14 +943,14 @@ | patterns.swift:41:5:41:18 | case ... | CaseStmt | patterns.swift:41:10:41:13 | ... is ... | CaseLabelItem | | patterns.swift:41:5:41:18 | case ... | CaseStmt | patterns.swift:41:18:41:18 | { ... } | BraceStmt | | patterns.swift:41:10:41:13 | ... is ... | CaseLabelItem | patterns.swift:41:10:41:13 | ... is ... | IsPattern | -| patterns.swift:41:10:41:13 | ... is ... | IsPattern | patterns.swift:41:13:41:13 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| patterns.swift:41:10:41:13 | ... is ... | IsPattern | patterns.swift:41:13:41:13 | Int | TypeRepr | | patterns.swift:41:18:41:18 | { ... } | BraceStmt | patterns.swift:41:18:41:18 | is pattern | StringLiteralExpr | | patterns.swift:42:5:42:27 | case ... | CaseStmt | patterns.swift:42:10:42:19 | let ... | CaseLabelItem | | patterns.swift:42:5:42:27 | case ... | CaseStmt | patterns.swift:42:27:42:27 | { ... } | BraceStmt | | patterns.swift:42:10:42:19 | let ... | BindingPattern | patterns.swift:42:14:42:19 | ... is ... | IsPattern | | patterns.swift:42:10:42:19 | let ... | CaseLabelItem | patterns.swift:42:10:42:19 | let ... | BindingPattern | | patterns.swift:42:14:42:19 | ... is ... | IsPattern | patterns.swift:42:14:42:14 | x | NamedPattern | -| patterns.swift:42:14:42:19 | ... is ... | IsPattern | patterns.swift:42:19:42:19 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| patterns.swift:42:14:42:19 | ... is ... | IsPattern | patterns.swift:42:19:42:19 | String | TypeRepr | | patterns.swift:42:27:42:27 | { ... } | BraceStmt | patterns.swift:42:27:42:27 | as pattern | StringLiteralExpr | | patterns.swift:43:5:43:13 | case ... | CaseStmt | patterns.swift:43:10:43:10 | _ | CaseLabelItem | | patterns.swift:43:5:43:13 | case ... | CaseStmt | patterns.swift:43:13:43:13 | { ... } | BraceStmt | @@ -986,14 +982,14 @@ | statements.swift:2:3:8:3 | for ... in ... { ... } | ForEachStmt | statements.swift:2:20:2:24 | ... call to ...(_:_:) ... | BinaryExpr | | statements.swift:2:3:8:3 | for ... in ... { ... } | ForEachStmt | statements.swift:2:26:8:3 | { ... } | BraceStmt | | statements.swift:2:20:2:24 | ... call to ...(_:_:) ... | BinaryExpr | statements.swift:2:21:2:21 | call to ...(_:_:) | DotSyntaxCallExpr | -| statements.swift:2:21:2:21 | Int.Type | TypeExpr | statements.swift:2:21:2:21 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:2:21:2:21 | Int.Type | TypeExpr | statements.swift:2:21:2:21 | Int | TypeRepr | | statements.swift:2:21:2:21 | call to ...(_:_:) | DotSyntaxCallExpr | statements.swift:2:21:2:21 | ...(_:_:) | DeclRefExpr | | statements.swift:2:26:8:3 | { ... } | BraceStmt | statements.swift:3:5:7:5 | if ... then { ... } else { ... } | IfStmt | | statements.swift:3:5:7:5 | if ... then { ... } else { ... } | IfStmt | statements.swift:3:8:3:13 | StmtCondition | StmtCondition | | statements.swift:3:5:7:5 | if ... then { ... } else { ... } | IfStmt | statements.swift:3:15:5:5 | { ... } | BraceStmt | | statements.swift:3:5:7:5 | if ... then { ... } else { ... } | IfStmt | statements.swift:5:12:7:5 | { ... } | BraceStmt | | statements.swift:3:8:3:13 | ... call to ==(_:_:) ... | BinaryExpr | statements.swift:3:10:3:10 | call to ==(_:_:) | DotSyntaxCallExpr | -| statements.swift:3:10:3:10 | Int.Type | TypeExpr | statements.swift:3:10:3:10 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:3:10:3:10 | Int.Type | TypeExpr | statements.swift:3:10:3:10 | Int | TypeRepr | | statements.swift:3:10:3:10 | call to ==(_:_:) | DotSyntaxCallExpr | statements.swift:3:10:3:10 | ==(_:_:) | DeclRefExpr | | statements.swift:3:15:5:5 | { ... } | BraceStmt | statements.swift:4:9:4:9 | break | BreakStmt | | statements.swift:5:12:7:5 | { ... } | BraceStmt | statements.swift:6:9:6:9 | continue | ContinueStmt | @@ -1004,14 +1000,14 @@ | statements.swift:10:17:10:24 | (...) | ParenExpr | statements.swift:10:18:10:22 | ... call to <(_:_:) ... | BinaryExpr | | statements.swift:10:18:10:18 | (Int) ... | LoadExpr | statements.swift:10:18:10:18 | i | DeclRefExpr | | statements.swift:10:18:10:22 | ... call to <(_:_:) ... | BinaryExpr | statements.swift:10:20:10:20 | call to <(_:_:) | DotSyntaxCallExpr | -| statements.swift:10:20:10:20 | Int.Type | TypeExpr | statements.swift:10:20:10:20 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:10:20:10:20 | Int.Type | TypeExpr | statements.swift:10:20:10:20 | Int | TypeRepr | | statements.swift:10:20:10:20 | call to <(_:_:) | DotSyntaxCallExpr | statements.swift:10:20:10:20 | <(_:_:) | DeclRefExpr | | statements.swift:10:26:12:3 | { ... } | BraceStmt | statements.swift:11:5:11:13 | ... = ... | AssignExpr | | statements.swift:11:5:11:13 | ... = ... | AssignExpr | statements.swift:11:5:11:5 | i | DeclRefExpr | | statements.swift:11:5:11:13 | ... = ... | AssignExpr | statements.swift:11:9:11:13 | ... call to +(_:_:) ... | BinaryExpr | | statements.swift:11:9:11:9 | (Int) ... | LoadExpr | statements.swift:11:9:11:9 | i | DeclRefExpr | | statements.swift:11:9:11:13 | ... call to +(_:_:) ... | BinaryExpr | statements.swift:11:11:11:11 | call to +(_:_:) | DotSyntaxCallExpr | -| statements.swift:11:11:11:11 | Int.Type | TypeExpr | statements.swift:11:11:11:11 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:11:11:11:11 | Int.Type | TypeExpr | statements.swift:11:11:11:11 | Int | TypeRepr | | statements.swift:11:11:11:11 | call to +(_:_:) | DotSyntaxCallExpr | statements.swift:11:11:11:11 | +(_:_:) | DeclRefExpr | | statements.swift:14:3:14:7 | ... = ... | AssignExpr | statements.swift:14:3:14:3 | i | DeclRefExpr | | statements.swift:14:3:14:7 | ... = ... | AssignExpr | statements.swift:14:7:14:7 | 0 | IntegerLiteralExpr | @@ -1022,12 +1018,12 @@ | statements.swift:16:5:16:13 | ... = ... | AssignExpr | statements.swift:16:9:16:13 | ... call to +(_:_:) ... | BinaryExpr | | statements.swift:16:9:16:9 | (Int) ... | LoadExpr | statements.swift:16:9:16:9 | i | DeclRefExpr | | statements.swift:16:9:16:13 | ... call to +(_:_:) ... | BinaryExpr | statements.swift:16:11:16:11 | call to +(_:_:) | DotSyntaxCallExpr | -| statements.swift:16:11:16:11 | Int.Type | TypeExpr | statements.swift:16:11:16:11 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:16:11:16:11 | Int.Type | TypeExpr | statements.swift:16:11:16:11 | Int | TypeRepr | | statements.swift:16:11:16:11 | call to +(_:_:) | DotSyntaxCallExpr | statements.swift:16:11:16:11 | +(_:_:) | DeclRefExpr | | statements.swift:17:11:17:18 | (...) | ParenExpr | statements.swift:17:12:17:16 | ... call to <(_:_:) ... | BinaryExpr | | statements.swift:17:12:17:12 | (Int) ... | LoadExpr | statements.swift:17:12:17:12 | i | DeclRefExpr | | statements.swift:17:12:17:16 | ... call to <(_:_:) ... | BinaryExpr | statements.swift:17:14:17:14 | call to <(_:_:) | DotSyntaxCallExpr | -| statements.swift:17:14:17:14 | Int.Type | TypeExpr | statements.swift:17:14:17:14 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:17:14:17:14 | Int.Type | TypeExpr | statements.swift:17:14:17:14 | Int | TypeRepr | | statements.swift:17:14:17:14 | call to <(_:_:) | DotSyntaxCallExpr | statements.swift:17:14:17:14 | <(_:_:) | DeclRefExpr | | statements.swift:19:3:23:3 | do { ... } catch { ... } | DoCatchStmt | statements.swift:19:6:21:3 | { ... } | BraceStmt | | statements.swift:19:3:23:3 | do { ... } catch { ... } | DoCatchStmt | statements.swift:21:5:23:3 | case ... | CaseStmt | @@ -1074,11 +1070,11 @@ | statements.swift:39:3:41:3 | guard ... else { ... } | GuardStmt | statements.swift:39:9:39:14 | StmtCondition | StmtCondition | | statements.swift:39:3:41:3 | guard ... else { ... } | GuardStmt | statements.swift:39:21:41:3 | { ... } | BraceStmt | | statements.swift:39:9:39:14 | ... call to !=(_:_:) ... | BinaryExpr | statements.swift:39:11:39:11 | call to !=(_:_:) | DotSyntaxCallExpr | -| statements.swift:39:11:39:11 | Int.Type | TypeExpr | statements.swift:39:11:39:11 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:39:11:39:11 | Int.Type | TypeExpr | statements.swift:39:11:39:11 | Int | TypeRepr | | statements.swift:39:11:39:11 | call to !=(_:_:) | DotSyntaxCallExpr | statements.swift:39:11:39:11 | !=(_:_:) | DeclRefExpr | | statements.swift:39:21:41:3 | { ... } | BraceStmt | statements.swift:40:5:40:19 | throw ... | ThrowStmt | | statements.swift:40:5:40:19 | throw ... | ThrowStmt | statements.swift:40:11:40:19 | (Error) ... | ErasureExpr | -| statements.swift:40:11:40:11 | AnError.Type | TypeExpr | statements.swift:40:11:40:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| statements.swift:40:11:40:11 | AnError.Type | TypeExpr | statements.swift:40:11:40:11 | AnError | TypeRepr | | statements.swift:40:11:40:19 | (Error) ... | ErasureExpr | statements.swift:40:11:40:19 | call to ... | DotSyntaxCallExpr | | statements.swift:40:11:40:19 | call to ... | DotSyntaxCallExpr | statements.swift:40:19:40:19 | failed | DeclRefExpr | | statements.swift:44:1:46:1 | defer { ... } | DeferStmt | statements.swift:44:7:46:1 | { ... } | BraceStmt | @@ -1143,7 +1139,7 @@ | statements.swift:64:1:64:15 | { ... } | BraceStmt | statements.swift:64:1:64:15 | var ... = ... | PatternBindingDecl | | statements.swift:64:1:64:15 | { ... } | TopLevelCodeDecl | statements.swift:64:1:64:15 | { ... } | BraceStmt | | statements.swift:64:5:64:11 | ... as ... | TypedPattern | statements.swift:64:5:64:5 | x | NamedPattern | -| statements.swift:64:5:64:11 | ... as ... | TypedPattern | statements.swift:64:8:64:11 | ...? | OptionalTypeRepr | +| statements.swift:64:5:64:11 | ... as ... | TypedPattern | statements.swift:64:8:64:11 | Int? | TypeRepr | | statements.swift:64:15:64:15 | (Int?) ... | InjectIntoOptionalExpr | statements.swift:64:15:64:15 | 4 | IntegerLiteralExpr | | statements.swift:65:1:66:1 | if ... then { ... } | IfStmt | statements.swift:65:4:65:19 | StmtCondition | StmtCondition | | statements.swift:65:1:66:1 | if ... then { ... } | IfStmt | statements.swift:65:21:66:1 | { ... } | BraceStmt | @@ -1172,9 +1168,9 @@ | statements.swift:71:1:72:1 | { ... } | TopLevelCodeDecl | statements.swift:71:1:72:1 | { ... } | BraceStmt | | statements.swift:71:29:71:38 | ... call to %(_:_:) ... | BinaryExpr | statements.swift:71:36:71:36 | call to %(_:_:) | DotSyntaxCallExpr | | statements.swift:71:29:71:43 | ... call to ==(_:_:) ... | BinaryExpr | statements.swift:71:40:71:40 | call to ==(_:_:) | DotSyntaxCallExpr | -| statements.swift:71:36:71:36 | Int.Type | TypeExpr | statements.swift:71:36:71:36 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:71:36:71:36 | Int.Type | TypeExpr | statements.swift:71:36:71:36 | Int | TypeRepr | | statements.swift:71:36:71:36 | call to %(_:_:) | DotSyntaxCallExpr | statements.swift:71:36:71:36 | %(_:_:) | DeclRefExpr | -| statements.swift:71:40:71:40 | Int.Type | TypeExpr | statements.swift:71:40:71:40 | FixedTypeRepr | FixedTypeRepr | +| statements.swift:71:40:71:40 | Int.Type | TypeExpr | statements.swift:71:40:71:40 | Int | TypeRepr | | statements.swift:71:40:71:40 | call to ==(_:_:) | DotSyntaxCallExpr | statements.swift:71:40:71:40 | ==(_:_:) | DeclRefExpr | | statements.swift:74:8:74:8 | init | ConstructorDecl | statements.swift:74:8:74:8 | x | ParamDecl | | statements.swift:75:3:75:11 | var ... = ... | PatternBindingDecl | statements.swift:75:7:75:11 | ... as ... | TypedPattern | @@ -1190,7 +1186,7 @@ | statements.swift:75:7:75:7 | { ... } | BraceStmt | file://:0:0:0:0 | return ... | ReturnStmt | | statements.swift:75:7:75:7 | { ... } | BraceStmt | statements.swift:75:7:75:7 | yield ... | YieldStmt | | statements.swift:75:7:75:11 | ... as ... | TypedPattern | statements.swift:75:7:75:7 | x | NamedPattern | -| statements.swift:75:7:75:11 | ... as ... | TypedPattern | statements.swift:75:11:75:11 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| statements.swift:75:7:75:11 | ... as ... | TypedPattern | statements.swift:75:11:75:11 | Int | TypeRepr | | statements.swift:76:3:84:3 | var ... = ... | PatternBindingDecl | statements.swift:76:7:76:19 | ... as ... | TypedPattern | | statements.swift:76:7:76:7 | hasModify | ConcreteVarDecl | statements.swift:76:7:76:7 | set | AccessorDecl | | statements.swift:76:7:76:7 | hasModify | ConcreteVarDecl | statements.swift:77:5:79:5 | (unnamed function decl) | AccessorDecl | @@ -1199,7 +1195,7 @@ | statements.swift:76:7:76:7 | set | AccessorDecl | statements.swift:76:7:76:7 | { ... } | BraceStmt | | statements.swift:76:7:76:7 | { ... } | BraceStmt | file://:0:0:0:0 | ... = ... | AssignExpr | | statements.swift:76:7:76:19 | ... as ... | TypedPattern | statements.swift:76:7:76:7 | hasModify | NamedPattern | -| statements.swift:76:7:76:19 | ... as ... | TypedPattern | statements.swift:76:19:76:19 | SimpleIdentTypeRepr | SimpleIdentTypeRepr | +| statements.swift:76:7:76:19 | ... as ... | TypedPattern | statements.swift:76:19:76:19 | Int | TypeRepr | | statements.swift:77:5:79:5 | (unnamed function decl) | AccessorDecl | statements.swift:77:13:79:5 | { ... } | BraceStmt | | statements.swift:77:13:79:5 | { ... } | BraceStmt | statements.swift:78:7:78:14 | yield ... | YieldStmt | | statements.swift:78:7:78:14 | yield ... | YieldStmt | statements.swift:78:13:78:14 | &... | InOutExpr | diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected new file mode 100644 index 00000000000..f11e0bbe6f2 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.expected @@ -0,0 +1,98 @@ +edges +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | +| UnsafeWebViewFetch.swift:103:30:103:84 | call to ... : | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | +| UnsafeWebViewFetch.swift:105:18:105:72 | call to ... : | UnsafeWebViewFetch.swift:106:25:106:25 | data | +| UnsafeWebViewFetch.swift:109:30:109:53 | call to ... : | UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:127:25:127:25 | "..." | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:138:47:138:56 | ...! | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:139:48:139:57 | ...! | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:140:47:140:57 | ...! | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:141:48:141:58 | ...! | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:153:85:153:94 | ...! | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:154:86:154:95 | ...! | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:174:25:174:25 | "..." | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:185:47:185:56 | ...! | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:186:48:186:57 | ...! | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:187:47:187:57 | ...! | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:188:48:188:58 | ...! | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:200:90:200:99 | ...! | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | UnsafeWebViewFetch.swift:201:91:201:100 | ...! | +| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | +| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | +nodes +| UnsafeWebViewFetch.swift:94:10:94:37 | try ... : | semmle.label | try ... : | +| UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | semmle.label | call to ... : | +| UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | semmle.label | try! ... | +| UnsafeWebViewFetch.swift:103:30:103:84 | call to ... : | semmle.label | call to ... : | +| UnsafeWebViewFetch.swift:105:18:105:72 | call to ... : | semmle.label | call to ... : | +| UnsafeWebViewFetch.swift:106:25:106:25 | data | semmle.label | data | +| UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | semmle.label | try! ... | +| UnsafeWebViewFetch.swift:109:30:109:53 | call to ... : | semmle.label | call to ... : | +| UnsafeWebViewFetch.swift:117:21:117:35 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | semmle.label | call to getRemoteData() | +| UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | semmle.label | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:127:25:127:25 | "..." | semmle.label | "..." | +| UnsafeWebViewFetch.swift:135:25:135:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:137:25:137:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:138:47:138:56 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:139:48:139:57 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:140:47:140:57 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:141:48:141:58 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:153:85:153:94 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:154:86:154:95 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:164:21:164:35 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | semmle.label | call to getRemoteData() | +| UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | semmle.label | ... call to +(_:_:) ... | +| UnsafeWebViewFetch.swift:174:25:174:25 | "..." | semmle.label | "..." | +| UnsafeWebViewFetch.swift:182:25:182:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:184:25:184:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:185:47:185:56 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:186:48:186:57 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:187:47:187:57 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | semmle.label | remoteString | +| UnsafeWebViewFetch.swift:188:48:188:58 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:200:90:200:99 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:201:91:201:100 | ...! | semmle.label | ...! | +| UnsafeWebViewFetch.swift:206:17:206:31 | call to getRemoteData() : | semmle.label | call to getRemoteData() : | +| UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | semmle.label | htmlData | +| UnsafeWebViewFetch.swift:211:25:211:25 | htmlData | semmle.label | htmlData | +subpaths +#select +| UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | UnsafeWebViewFetch.swift:103:30:103:84 | call to ... : | UnsafeWebViewFetch.swift:103:25:103:84 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:106:25:106:25 | data | UnsafeWebViewFetch.swift:105:18:105:72 | call to ... : | UnsafeWebViewFetch.swift:106:25:106:25 | data | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | UnsafeWebViewFetch.swift:109:30:109:53 | call to ... : | UnsafeWebViewFetch.swift:109:25:109:53 | try! ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:120:25:120:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:121:25:121:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:124:25:124:51 | ... call to +(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:127:25:127:25 | "..." | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:127:25:127:25 | "..." | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:139:25:139:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:141:25:141:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:167:25:167:39 | call to getRemoteData() | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:168:25:168:25 | remoteString | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:171:25:171:51 | ... call to +(_:_:) ... | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:174:25:174:25 | "..." | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:174:25:174:25 | "..." | Tainted data is used in a WebView fetch without restricting the base URL. | +| UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:186:25:186:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:188:25:188:25 | remoteString | Tainted data is used in a WebView fetch with a tainted base URL. | +| UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | UnsafeWebViewFetch.swift:94:14:94:37 | call to ... : | UnsafeWebViewFetch.swift:210:25:210:25 | htmlData | Tainted data is used in a WebView fetch without restricting the base URL. | diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref new file mode 100644 index 00000000000..a5c8cb457a0 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.qlref @@ -0,0 +1 @@ +queries/Security/CWE-079/UnsafeWebViewFetch.ql \ No newline at end of file diff --git a/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift new file mode 100644 index 00000000000..6ee075b4d3d --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-079/UnsafeWebViewFetch.swift @@ -0,0 +1,217 @@ + +// --- stubs --- + +class NSObject +{ +} + +class URL +{ + init?(string: String) {} + init?(string: String, relativeTo: URL?) {} +} + +extension String { + init(contentsOf: URL) throws { + var data = "" + + // ... + + self.init(data) + } +} + +class NSURLRequest : NSObject +{ + enum CachePolicy : UInt + { + case useProtocolCachePolicy + } +} + +typealias TimeInterval = Double + +class URLRequest +{ + typealias CachePolicy = NSURLRequest.CachePolicy + + init(url: URL, cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy, timeoutInterval: TimeInterval = 60.0) {} +} + +class Data +{ + init(_ elements: S) {} +} + +class WKNavigation : NSObject +{ +} + +class UIResponder : NSObject +{ +} + +class UIView : UIResponder +{ +} + +class UIWebView : UIView +{ + func loadRequest(_ request: URLRequest) {} // deprecated + + func load(_ data: Data, mimeType MIMEType: String, textEncodingName: String, baseURL: URL) {} // deprecated + + func loadHTMLString(_ string: String, baseURL: URL?) {} // deprecated +} + +class WKWebView : UIView +{ + func load(_ request: URLRequest) -> WKNavigation? { + // ... + + return WKNavigation() + } + + func load(_ data: Data, mimeType MIMEType: String, characterEncodingName: String, baseURL: URL) -> WKNavigation? { + // ... + + return WKNavigation() + } + + func loadHTMLString(_ string: String, baseURL: URL?) -> WKNavigation? { + // ... + + return WKNavigation() + } +} + +// --- tests --- + +func getRemoteData() -> String { + let url = URL(string: "http://example.com/") + do + { + return try String(contentsOf: url!) + } catch { + return "" + } +} + +func testSimpleFlows() { + let webview = UIWebView() + + webview.loadHTMLString(try! String(contentsOf: URL(string: "http://example.com/")!), baseURL: nil) // BAD + + let data = try! String(contentsOf: URL(string: "http://example.com/")!) + webview.loadHTMLString(data, baseURL: nil) // BAD + + let url = URL(string: "http://example.com/") + webview.loadHTMLString(try! String(contentsOf: url!), baseURL: nil) // BAD +} + +func testUIWebView() { + let webview = UIWebView() + + let localString = "

    Local HTML

    " + let localStringFragment = "

    Local HTML

    " + let remoteString = getRemoteData() + + webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD: HTML contains remote input, may access local secrets + webview.loadHTMLString(remoteString, baseURL: nil) // BAD + + webview.loadHTMLString("" + localStringFragment + "", baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD + + webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD + + let localSafeURL = URL(string: "about:blank") + let localURL = URL(string: "http://example.com/") + let remoteURL = URL(string: remoteString) + let remoteURL2 = URL(string: "/path", relativeTo: remoteURL) + + webview.loadHTMLString(localString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.loadHTMLString(remoteString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified + webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified + webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local + webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD + webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local + webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD + + let localRequest = URLRequest(url: localURL!) + let remoteRequest = URLRequest(url: remoteURL!) + + webview.loadRequest(localRequest) // GOOD: loadRequest is out of scope as it has no baseURL + webview.loadRequest(remoteRequest) // GOOD: loadRequest is out of scope as it has no baseURL + + let localData = Data(localString.utf8) + let remoteData = Data(remoteString.utf8) + webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local + webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.load(localData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local + webview.load(remoteData, mimeType: "text/html", textEncodingName: "utf-8", baseURL: remoteURL!) // BAD [NOT DETECTED] +} + +func testWKWebView() { + let webview = WKWebView() + // note: `WKWebView` is safer than `UIWebView` as it has better security configuration options + // and is more locked down by default. + + let localString = "

    Local HTML

    " + let localStringFragment = "

    Local HTML

    " + let remoteString = getRemoteData() + + webview.loadHTMLString(localString, baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString(getRemoteData(), baseURL: nil) // BAD + webview.loadHTMLString(remoteString, baseURL: nil) // BAD + + webview.loadHTMLString("" + localStringFragment + "", baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString("" + remoteString + "", baseURL: nil) // BAD + + webview.loadHTMLString("\(localStringFragment)", baseURL: nil) // GOOD: the HTML data is local + webview.loadHTMLString("\(remoteString)", baseURL: nil) // BAD + + let localSafeURL = URL(string: "about:blank") + let localURL = URL(string: "http://example.com/") + let remoteURL = URL(string: remoteString) + let remoteURL2 = URL(string: "/path", relativeTo: remoteURL) + + webview.loadHTMLString(localString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.loadHTMLString(remoteString, baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.loadHTMLString(localString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified + webview.loadHTMLString(remoteString, baseURL: localURL!) // GOOD: a presumed safe baseURL is specified + webview.loadHTMLString(localString, baseURL: remoteURL!) // GOOD: the HTML data is local + webview.loadHTMLString(remoteString, baseURL: remoteURL!) // BAD + webview.loadHTMLString(localString, baseURL: remoteURL2!) // GOOD: the HTML data is local + webview.loadHTMLString(remoteString, baseURL: remoteURL2!) // BAD + + let localRequest = URLRequest(url: localURL!) + let remoteRequest = URLRequest(url: remoteURL!) + + webview.load(localRequest) // GOOD: loadRequest is out of scope as it has no baseURL + webview.load(remoteRequest) // GOOD: loadRequest is out of scope as it has no baseURL + + let localData = Data(localString.utf8) + let remoteData = Data(remoteString.utf8) + webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: the data is local + webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: localSafeURL!) // GOOD: a safe baseURL is specified + webview.load(localData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // GOOD: the HTML data is local + webview.load(remoteData, mimeType: "text/html", characterEncodingName: "utf-8", baseURL: remoteURL!) // BAD [NOT DETECTED] +} + +func testQHelpExamples() { + let webview = UIWebView() + let htmlData = getRemoteData() + + // ... + + webview.loadHTMLString(htmlData, baseURL: nil) // BAD + webview.loadHTMLString(htmlData, baseURL: URL(string: "about:blank")) // GOOD +} + +testSimpleFlows() +testUIWebView() +testWKWebView() +testQHelpExamples() diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected index 1bb4de15325..90fa818d56a 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.expected @@ -1,36 +1,84 @@ edges -| StringLengthConflation.swift:101:34:101:36 | .count : | StringLengthConflation.swift:101:34:101:44 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:102:36:102:38 | .count : | StringLengthConflation.swift:102:36:102:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:107:36:107:38 | .count : | StringLengthConflation.swift:107:36:107:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:108:38:108:40 | .count : | StringLengthConflation.swift:108:38:108:48 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:113:34:113:36 | .count : | StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:114:36:114:38 | .count : | StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | -| StringLengthConflation.swift:120:28:120:30 | .count : | StringLengthConflation.swift:120:28:120:38 | ... call to -(_:_:) ... | +| StringLengthConflation2.swift:37:34:37:36 | .count : | StringLengthConflation2.swift:37:34:37:44 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:60:47:60:50 | .length : | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | +| StringLengthConflation.swift:66:33:66:36 | .length : | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | +| StringLengthConflation.swift:96:28:96:31 | .length : | StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:100:27:100:30 | .length : | StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:104:25:104:28 | .length : | StringLengthConflation.swift:104:25:104:37 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:108:25:108:28 | .length : | StringLengthConflation.swift:108:25:108:37 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:114:23:114:26 | .length : | StringLengthConflation.swift:114:23:114:35 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:120:22:120:25 | .length : | StringLengthConflation.swift:120:22:120:34 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:125:34:125:36 | .count : | StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:126:36:126:38 | .count : | StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:131:36:131:38 | .count : | StringLengthConflation.swift:131:36:131:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:132:38:132:40 | .count : | StringLengthConflation.swift:132:38:132:48 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:137:34:137:36 | .count : | StringLengthConflation.swift:137:34:137:44 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:138:36:138:38 | .count : | StringLengthConflation.swift:138:36:138:46 | ... call to -(_:_:) ... | +| StringLengthConflation.swift:144:28:144:30 | .count : | StringLengthConflation.swift:144:28:144:38 | ... call to -(_:_:) ... | nodes +| StringLengthConflation2.swift:37:34:37:36 | .count : | semmle.label | .count : | +| StringLengthConflation2.swift:37:34:37:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:53:43:53:46 | .length | semmle.label | .length | +| StringLengthConflation.swift:54:43:54:50 | .count | semmle.label | .count | +| StringLengthConflation.swift:55:43:55:51 | .count | semmle.label | .count | +| StringLengthConflation.swift:56:43:56:60 | .count | semmle.label | .count | +| StringLengthConflation.swift:60:47:60:50 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | semmle.label | ... call to /(_:_:) ... | +| StringLengthConflation.swift:66:33:66:36 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | semmle.label | ... call to /(_:_:) ... | | StringLengthConflation.swift:72:33:72:35 | .count | semmle.label | .count | | StringLengthConflation.swift:78:47:78:49 | .count | semmle.label | .count | -| StringLengthConflation.swift:101:34:101:36 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:101:34:101:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:102:36:102:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:102:36:102:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:107:36:107:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:107:36:107:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:108:38:108:40 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:108:38:108:48 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:113:34:113:36 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:114:36:114:38 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | -| StringLengthConflation.swift:120:28:120:30 | .count : | semmle.label | .count : | -| StringLengthConflation.swift:120:28:120:38 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:79:47:79:54 | .count | semmle.label | .count | +| StringLengthConflation.swift:81:47:81:64 | .count | semmle.label | .count | +| StringLengthConflation.swift:96:28:96:31 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:100:27:100:30 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:104:25:104:28 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:104:25:104:37 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:108:25:108:28 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:108:25:108:37 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:114:23:114:26 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:114:23:114:35 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:120:22:120:25 | .length : | semmle.label | .length : | +| StringLengthConflation.swift:120:22:120:34 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:125:34:125:36 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:126:36:126:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:131:36:131:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:131:36:131:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:132:38:132:40 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:132:38:132:48 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:137:34:137:36 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:137:34:137:44 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:138:36:138:38 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:138:36:138:46 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | +| StringLengthConflation.swift:144:28:144:30 | .count : | semmle.label | .count : | +| StringLengthConflation.swift:144:28:144:38 | ... call to -(_:_:) ... | semmle.label | ... call to -(_:_:) ... | subpaths #select +| StringLengthConflation2.swift:37:34:37:44 | ... call to -(_:_:) ... | StringLengthConflation2.swift:37:34:37:36 | .count : | StringLengthConflation2.swift:37:34:37:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | StringLengthConflation.swift:53:43:53:46 | .length | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:54:43:54:50 | .count | StringLengthConflation.swift:54:43:54:50 | .count | StringLengthConflation.swift:54:43:54:50 | .count | This String.utf8 length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:55:43:55:51 | .count | StringLengthConflation.swift:55:43:55:51 | .count | StringLengthConflation.swift:55:43:55:51 | .count | This String.utf16 length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:56:43:56:60 | .count | StringLengthConflation.swift:56:43:56:60 | .count | StringLengthConflation.swift:56:43:56:60 | .count | This String.unicodeScalars length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | StringLengthConflation.swift:60:47:60:50 | .length : | StringLengthConflation.swift:60:47:60:59 | ... call to /(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | StringLengthConflation.swift:66:33:66:36 | .length : | StringLengthConflation.swift:66:33:66:45 | ... call to /(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | StringLengthConflation.swift:72:33:72:35 | .count | This String length is used in an NSString, but it may not be equivalent. | | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | StringLengthConflation.swift:78:47:78:49 | .count | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:101:34:101:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:101:34:101:36 | .count : | StringLengthConflation.swift:101:34:101:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:102:36:102:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:102:36:102:38 | .count : | StringLengthConflation.swift:102:36:102:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:107:36:107:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:107:36:107:38 | .count : | StringLengthConflation.swift:107:36:107:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:108:38:108:48 | ... call to -(_:_:) ... | StringLengthConflation.swift:108:38:108:40 | .count : | StringLengthConflation.swift:108:38:108:48 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:113:34:113:36 | .count : | StringLengthConflation.swift:113:34:113:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:114:36:114:38 | .count : | StringLengthConflation.swift:114:36:114:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | -| StringLengthConflation.swift:120:28:120:38 | ... call to -(_:_:) ... | StringLengthConflation.swift:120:28:120:30 | .count : | StringLengthConflation.swift:120:28:120:38 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:79:47:79:54 | .count | StringLengthConflation.swift:79:47:79:54 | .count | StringLengthConflation.swift:79:47:79:54 | .count | This String.utf8 length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:81:47:81:64 | .count | StringLengthConflation.swift:81:47:81:64 | .count | StringLengthConflation.swift:81:47:81:64 | .count | This String.unicodeScalars length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | StringLengthConflation.swift:96:28:96:31 | .length : | StringLengthConflation.swift:96:28:96:40 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | StringLengthConflation.swift:100:27:100:30 | .length : | StringLengthConflation.swift:100:27:100:39 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:104:25:104:37 | ... call to -(_:_:) ... | StringLengthConflation.swift:104:25:104:28 | .length : | StringLengthConflation.swift:104:25:104:37 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:108:25:108:37 | ... call to -(_:_:) ... | StringLengthConflation.swift:108:25:108:28 | .length : | StringLengthConflation.swift:108:25:108:37 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:114:23:114:35 | ... call to -(_:_:) ... | StringLengthConflation.swift:114:23:114:26 | .length : | StringLengthConflation.swift:114:23:114:35 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:120:22:120:34 | ... call to -(_:_:) ... | StringLengthConflation.swift:120:22:120:25 | .length : | StringLengthConflation.swift:120:22:120:34 | ... call to -(_:_:) ... | This NSString length is used in a String, but it may not be equivalent. | +| StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:125:34:125:36 | .count : | StringLengthConflation.swift:125:34:125:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:126:36:126:38 | .count : | StringLengthConflation.swift:126:36:126:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:131:36:131:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:131:36:131:38 | .count : | StringLengthConflation.swift:131:36:131:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:132:38:132:48 | ... call to -(_:_:) ... | StringLengthConflation.swift:132:38:132:40 | .count : | StringLengthConflation.swift:132:38:132:48 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:137:34:137:44 | ... call to -(_:_:) ... | StringLengthConflation.swift:137:34:137:36 | .count : | StringLengthConflation.swift:137:34:137:44 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:138:36:138:46 | ... call to -(_:_:) ... | StringLengthConflation.swift:138:36:138:38 | .count : | StringLengthConflation.swift:138:36:138:46 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | +| StringLengthConflation.swift:144:28:144:38 | ... call to -(_:_:) ... | StringLengthConflation.swift:144:28:144:30 | .count : | StringLengthConflation.swift:144:28:144:38 | ... call to -(_:_:) ... | This String length is used in an NSString, but it may not be equivalent. | diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift index 8cb698009b6..4321b661fa4 100644 --- a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation.swift @@ -28,6 +28,7 @@ class NSMutableString : NSString class NSRange { init(location: Int, length: Int) { self.description = "" } + init(_ r: R, in: S) { self.description = "" } private(set) var description: String } @@ -36,7 +37,6 @@ func NSMakeRange(_ loc: Int, _ len: Int) -> NSRange { return NSRange(location: l - // --- tests --- func test(s: String) { @@ -50,20 +50,20 @@ func test(s: String) { // --- constructing a String.Index from integer --- let ix1 = String.Index(encodedOffset: s.count) // GOOD - let ix2 = String.Index(encodedOffset: ns.length) // BAD: NSString length used in String.Index [NOT DETECTED] - let ix3 = String.Index(encodedOffset: s.utf8.count) // BAD: String.utf8 length used in String.Index [NOT DETECTED] - let ix4 = String.Index(encodedOffset: s.utf16.count) // BAD: String.utf16 length used in String.Index [NOT DETECTED] - let ix5 = String.Index(encodedOffset: s.unicodeScalars.count) // BAD: string.unicodeScalars length used in String.Index [NOT DETECTED] + let ix2 = String.Index(encodedOffset: ns.length) // BAD: NSString length used in String.Index + let ix3 = String.Index(encodedOffset: s.utf8.count) // BAD: String.utf8 length used in String.Index + let ix4 = String.Index(encodedOffset: s.utf16.count) // BAD: String.utf16 length used in String.Index + let ix5 = String.Index(encodedOffset: s.unicodeScalars.count) // BAD: string.unicodeScalars length used in String.Index print("String.Index '\(ix1.encodedOffset)' / '\(ix2.encodedOffset)' '\(ix3.encodedOffset)' '\(ix4.encodedOffset)' '\(ix5.encodedOffset)'") let ix6 = s.index(s.startIndex, offsetBy: s.count / 2) // GOOD - let ix7 = s.index(s.startIndex, offsetBy: ns.length / 2) // BAD: NSString length used in String.Index [NOT DETECTED] + let ix7 = s.index(s.startIndex, offsetBy: ns.length / 2) // BAD: NSString length used in String.Index print("index '\(ix6.encodedOffset)' / '\(ix7.encodedOffset)'") var ix8 = s.startIndex s.formIndex(&ix8, offsetBy: s.count / 2) // GOOD var ix9 = s.startIndex - s.formIndex(&ix9, offsetBy: ns.length / 2) // BAD: NSString length used in String.Index [NOT DETECTED] + s.formIndex(&ix9, offsetBy: ns.length / 2) // BAD: NSString length used in String.Index print("formIndex '\(ix8.encodedOffset)' / '\(ix9.encodedOffset)'") // --- constructing an NSRange from integers --- @@ -76,26 +76,50 @@ func test(s: String) { let range5 = NSRange(location: 0, length: ns.length) // GOOD let range6 = NSRange(location: 0, length: s.count) // BAD: String length used in NSMakeRange - print("NSRange '\(range5.description)' / '\(range6.description)'") + let range7 = NSRange(location: 0, length: s.utf8.count) // BAD: String.utf8 length used in NSMakeRange + let range8 = NSRange(location: 0, length: s.utf16.count) // GOOD: String.utf16 length and NSRange count are equivalent + let range9 = NSRange(location: 0, length: s.unicodeScalars.count) // BAD: String.unicodeScalars length used in NSMakeRange + print("NSRange '\(range5.description)' / '\(range6.description)' '\(range7.description)' '\(range8.description)' '\(range9.description)'") + + // --- converting Range to NSRange --- + + let range10 = s.startIndex ..< s.endIndex + let range11 = NSRange(range10, in: s) // GOOD + let location = s.distance(from: s.startIndex, to: range10.lowerBound) + let length = s.distance(from: range10.lowerBound, to: range10.upperBound) + let range12 = NSRange(location: location, length: length) // BAD [NOT DETECTED] + print("NSRange '\(range11.description)' / '\(range12.description)'") // --- String operations using an integer directly --- let str1 = s.dropFirst(s.count - 1) // GOOD - let str2 = s.dropFirst(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + let str2 = s.dropFirst(ns.length - 1) // BAD: NSString length used in String print("dropFirst '\(str1)' / '\(str2)'") let str3 = s.dropLast(s.count - 1) // GOOD - let str4 = s.dropLast(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + let str4 = s.dropLast(ns.length - 1) // BAD: NSString length used in String print("dropLast '\(str3)' / '\(str4)'") let str5 = s.prefix(s.count - 1) // GOOD - let str6 = s.prefix(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + let str6 = s.prefix(ns.length - 1) // BAD: NSString length used in String print("prefix '\(str5)' / '\(str6)'") let str7 = s.suffix(s.count - 1) // GOOD - let str8 = s.suffix(ns.length - 1) // BAD: NSString length used in String [NOT DETECTED] + let str8 = s.suffix(ns.length - 1) // BAD: NSString length used in String print("suffix '\(str7)' / '\(str8)'") + var str9 = s + str9.removeFirst(s.count - 1) // GOOD + var str10 = s + str10.removeFirst(ns.length - 1) // BAD: NSString length used in String + print("removeFirst '\(str9)' / '\(str10)'") + + var str11 = s + str11.removeLast(s.count - 1) // GOOD + var str12 = s + str12.removeLast(ns.length - 1) // BAD: NSString length used in String + print("removeLast '\(str11)' / '\(str12)'") + let nstr1 = ns.character(at: ns.length - 1) // GOOD let nmstr1 = nms.character(at: nms.length - 1) // GOOD let nstr2 = ns.character(at: s.count - 1) // BAD: String length used in NSString diff --git a/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation2.swift b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation2.swift new file mode 100644 index 00000000000..39b1456e604 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-135/StringLengthConflation2.swift @@ -0,0 +1,42 @@ +// this test is in a separate file, because we want to test with a slightly different library +// implementation. In this version, some of the functions of `NSString` are in fact implemented +// in a base class `NSStringBase`. + +// --- stubs --- + +func print(_ items: Any...) {} + +typealias unichar = UInt16 + +class NSObject +{ +} + +class NSStringBase : NSObject +{ + func substring(from: Int) -> String { return "" } +} + +class NSString : NSStringBase +{ + init(string: String) { length = string.count } + + func substring(to: Int) -> String { return "" } + + private(set) var length: Int +} + +// --- tests --- + +func test(s: String) { + let ns = NSString(string: s) + + let nstr1 = ns.substring(from: ns.length - 1) // GOOD + let nstr2 = ns.substring(from: s.count - 1) // BAD: String length used in NSString [NOT DETECTED] + let nstr3 = ns.substring(to: ns.length - 1) // GOOD + let nstr4 = ns.substring(to: s.count - 1) // BAD: String length used in NSString + print("substrings '\(nstr1)' '\(nstr2)' / '\(nstr3)' '\(nstr4)'") +} + +// `begin :thumbsup: end`, with thumbs up emoji and skin tone modifier +test(s: "begin \u{0001F44D}\u{0001F3FF} end")