mirror of
https://github.com/github/codeql.git
synced 2026-07-06 12:05:31 +02:00
Compare commits
1 Commits
codeql-cli
...
nickrolfe/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11bcad37af |
@@ -1,25 +1,11 @@
|
|||||||
{
|
{ "provide": [ "ruby/.codeqlmanifest.json",
|
||||||
"provide": [
|
|
||||||
"*/ql/src/qlpack.yml",
|
"*/ql/src/qlpack.yml",
|
||||||
"*/ql/lib/qlpack.yml",
|
"*/ql/lib/qlpack.yml",
|
||||||
"*/ql/test/qlpack.yml",
|
"*/ql/test/qlpack.yml",
|
||||||
|
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
|
||||||
"*/ql/examples/qlpack.yml",
|
"*/ql/examples/qlpack.yml",
|
||||||
"*/upgrades/qlpack.yml",
|
"*/upgrades/qlpack.yml",
|
||||||
"cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml",
|
|
||||||
"javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml",
|
"javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml",
|
||||||
"javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml",
|
"javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml",
|
||||||
"misc/legacy-support/*/qlpack.yml",
|
"misc/legacy-support/*/qlpack.yml",
|
||||||
"misc/suite-helpers/qlpack.yml",
|
"misc/suite-helpers/qlpack.yml" ] }
|
||||||
"ruby/extractor-pack/codeql-extractor.yml",
|
|
||||||
"ruby/ql/consistency-queries/qlpack.yml",
|
|
||||||
"ql/ql/consistency-queries/qlpack.yml",
|
|
||||||
"ql/extractor-pack/codeql-extractor.yml"
|
|
||||||
],
|
|
||||||
"versionPolicies": {
|
|
||||||
"default": {
|
|
||||||
"requireChangeNotes": true,
|
|
||||||
"committedPrereleaseSuffix": "dev",
|
|
||||||
"committedVersion": "nextPatchRelease"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
4
.github/actions/fetch-codeql/action.yml
vendored
4
.github/actions/fetch-codeql/action.yml
vendored
@@ -8,7 +8,7 @@ runs:
|
|||||||
run: |
|
run: |
|
||||||
LATEST=$(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | grep -v beta | sort --version-sort | tail -1)
|
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"
|
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$LATEST"
|
||||||
unzip -q -d "${RUNNER_TEMP}" codeql-linux64.zip
|
unzip -q codeql-linux64.zip
|
||||||
echo "${RUNNER_TEMP}/codeql" >> "${GITHUB_PATH}"
|
echo "${{ github.workspace }}/codeql" >> $GITHUB_PATH
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
|||||||
3
.github/labeler.yml
vendored
3
.github/labeler.yml
vendored
@@ -26,6 +26,3 @@ documentation:
|
|||||||
- "**/*.qhelp"
|
- "**/*.qhelp"
|
||||||
- "**/*.md"
|
- "**/*.md"
|
||||||
- docs/**/*
|
- docs/**/*
|
||||||
|
|
||||||
"QL-for-QL":
|
|
||||||
- ql/**/*
|
|
||||||
1
.github/workflows/check-change-note.yml
vendored
1
.github/workflows/check-change-note.yml
vendored
@@ -7,7 +7,6 @@ on:
|
|||||||
- "*/ql/src/**/*.ql"
|
- "*/ql/src/**/*.ql"
|
||||||
- "*/ql/src/**/*.qll"
|
- "*/ql/src/**/*.qll"
|
||||||
- "!**/experimental/**"
|
- "!**/experimental/**"
|
||||||
- "!ql/**"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-change-note:
|
check-change-note:
|
||||||
|
|||||||
31
.github/workflows/post-pr-comment.yml
vendored
31
.github/workflows/post-pr-comment.yml
vendored
@@ -1,31 +0,0 @@
|
|||||||
name: Post pull-request comment
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows: ["Query help preview"]
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
post_comment:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Download artifact
|
|
||||||
run: gh run download "${WORKFLOW_RUN_ID}" --repo "${GITHUB_REPOSITORY}" --name "comment"
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
|
||||||
WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }}
|
|
||||||
- run: |
|
|
||||||
PR="$(grep -o '^[0-9]\+$' pr.txt)"
|
|
||||||
PR_HEAD_SHA="$(gh api "/repos/${GITHUB_REPOSITORY}/pulls/${PR}" --jq .head.sha)"
|
|
||||||
# Check that the pull-request head SHA matches the head SHA of the workflow run
|
|
||||||
if [ "${WORKFLOW_RUN_HEAD_SHA}" != "${PR_HEAD_SHA}" ]; then
|
|
||||||
echo "PR head SHA ${PR_HEAD_SHA} does not match workflow_run event SHA ${WORKFLOW_RUN_HEAD_SHA}. Stopping." 1>&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
gh pr comment "${PR}" --repo "${GITHUB_REPOSITORY}" -F comment.txt
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
|
||||||
WORKFLOW_RUN_HEAD_SHA: ${{ github.event.workflow_run.head_commit.id }}
|
|
||||||
52
.github/workflows/qhelp-pr-preview.yml
vendored
52
.github/workflows/qhelp-pr-preview.yml
vendored
@@ -1,13 +1,10 @@
|
|||||||
name: Query help preview
|
name: Query help preview
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- "rc/*"
|
- 'rc/*'
|
||||||
paths:
|
paths:
|
||||||
- "ruby/**/*.qhelp"
|
- "ruby/**/*.qhelp"
|
||||||
|
|
||||||
@@ -15,49 +12,28 @@ jobs:
|
|||||||
qhelp:
|
qhelp:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- run: echo "${{ github.event.number }}" > pr.txt
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: comment
|
|
||||||
path: pr.txt
|
|
||||||
retention-days: 1
|
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
persist-credentials: false
|
|
||||||
- uses: ./.github/actions/fetch-codeql
|
|
||||||
- name: Determine changed files
|
- name: Determine changed files
|
||||||
id: changes
|
id: changes
|
||||||
run: |
|
run: |
|
||||||
(git diff -z --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep -z '.qhelp$' | grep -z -v '.inc.qhelp';
|
echo -n "::set-output name=qhelp_files::"
|
||||||
git diff -z --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep -z '.inc.qhelp$' | xargs --null -rn1 basename | xargs --null -rn1 git grep -z -l) |
|
(git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep .qhelp$ | grep -v .inc.qhelp;
|
||||||
grep -z '.qhelp$' | grep -z -v '^-' | sort -z -u > "${RUNNER_TEMP}/paths.txt"
|
git diff --name-only --diff-filter=ACMRT HEAD~1 HEAD | grep .inc.qhelp$ | xargs -d '\n' -rn1 basename | xargs -d '\n' -rn1 git grep -l) |
|
||||||
|
sort -u | xargs -d '\n' -n1 printf "'%s' "
|
||||||
|
|
||||||
|
- uses: ./.github/actions/fetch-codeql
|
||||||
|
|
||||||
- name: QHelp preview
|
- name: QHelp preview
|
||||||
|
if: ${{ steps.changes.outputs.qhelp_files }}
|
||||||
run: |
|
run: |
|
||||||
EXIT_CODE=0
|
( echo "QHelp previews:";
|
||||||
echo "QHelp previews:" > comment.txt
|
for path in ${{ steps.changes.outputs.qhelp_files }} ; do
|
||||||
while read -r -d $'\0' path; do
|
|
||||||
if [ ! -f "${path}" ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "<details> <summary>${path}</summary>"
|
echo "<details> <summary>${path}</summary>"
|
||||||
echo
|
echo
|
||||||
codeql generate query-help --format=markdown -- "./${path}" 2> errors.txt || EXIT_CODE="$?"
|
codeql generate query-help --format=markdown ${path}
|
||||||
if [ -s errors.txt ]; then
|
|
||||||
echo "# errors/warnings:"
|
|
||||||
echo '```'
|
|
||||||
cat errors.txt
|
|
||||||
cat errors.txt 1>&2
|
|
||||||
echo '```'
|
|
||||||
fi
|
|
||||||
echo "</details>"
|
echo "</details>"
|
||||||
done < "${RUNNER_TEMP}/paths.txt" >> comment.txt
|
done) | gh pr comment "${{ github.event.pull_request.number }}" -F -
|
||||||
exit "${EXIT_CODE}"
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
- if: always()
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: comment
|
|
||||||
path: comment.txt
|
|
||||||
retention-days: 1
|
|
||||||
|
|||||||
192
.github/workflows/ql-for-ql-build.yml
vendored
192
.github/workflows/ql-for-ql-build.yml
vendored
@@ -1,192 +0,0 @@
|
|||||||
name: Run QL for QL
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
queries:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Find codeql
|
|
||||||
id: find-codeql
|
|
||||||
uses: github/codeql-action/init@erik-krogh/ql
|
|
||||||
with:
|
|
||||||
languages: javascript # does not matter
|
|
||||||
- name: Get CodeQL version
|
|
||||||
id: get-codeql-version
|
|
||||||
run: |
|
|
||||||
echo "::set-output name=version::$("${CODEQL}" --version | head -n 1 | rev | cut -d " " -f 1 | rev)"
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
|
||||||
- name: Cache queries
|
|
||||||
id: cache-queries
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ${{ runner.temp }}/query-pack.zip
|
|
||||||
key: queries-${{ hashFiles('ql/**/*.ql*') }}-${{ hashFiles('ql/ql/src/ql.dbscheme*') }}-${{ steps.get-codeql-version.outputs.version }}
|
|
||||||
- name: Build query pack
|
|
||||||
if: steps.cache-queries.outputs.cache-hit != 'true'
|
|
||||||
run: |
|
|
||||||
cd ql/ql/src
|
|
||||||
"${CODEQL}" pack create
|
|
||||||
cd .codeql/pack/codeql/ql-all/0.0.0
|
|
||||||
zip "${PACKZIP}" -r .
|
|
||||||
env:
|
|
||||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
|
||||||
PACKZIP: ${{ runner.temp }}/query-pack.zip
|
|
||||||
- name: Upload query pack
|
|
||||||
uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: query-pack-zip
|
|
||||||
path: ${{ runner.temp }}/query-pack.zip
|
|
||||||
|
|
||||||
extractors:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Cache entire extractor
|
|
||||||
id: cache-extractor
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
ql/target/release/ql-autobuilder
|
|
||||||
ql/target/release/ql-autobuilder.exe
|
|
||||||
ql/target/release/ql-extractor
|
|
||||||
ql/target/release/ql-extractor.exe
|
|
||||||
key: ${{ runner.os }}-extractor-${{ hashFiles('ql/**/Cargo.lock') }}-${{ hashFiles('ql/**/*.rs') }}
|
|
||||||
- name: Cache cargo
|
|
||||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry
|
|
||||||
~/.cargo/git
|
|
||||||
ql/target
|
|
||||||
key: ${{ runner.os }}-rust-cargo-${{ hashFiles('ql/**/Cargo.lock') }}
|
|
||||||
- name: Check formatting
|
|
||||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
|
||||||
run: cd ql; cargo fmt --all -- --check
|
|
||||||
- name: Build
|
|
||||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
|
||||||
run: cd ql; cargo build --verbose
|
|
||||||
- name: Run tests
|
|
||||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
|
||||||
run: cd ql; cargo test --verbose
|
|
||||||
- name: Release build
|
|
||||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
|
||||||
run: cd ql; cargo build --release
|
|
||||||
- name: Generate dbscheme
|
|
||||||
if: steps.cache-extractor.outputs.cache-hit != 'true'
|
|
||||||
run: ql/target/release/ql-generator --dbscheme ql/ql/src/ql.dbscheme --library ql/ql/src/codeql_ql/ast/internal/TreeSitter.qll
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: extractor-ubuntu-latest
|
|
||||||
path: |
|
|
||||||
ql/target/release/ql-autobuilder
|
|
||||||
ql/target/release/ql-autobuilder.exe
|
|
||||||
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@v2
|
|
||||||
- uses: actions/download-artifact@v2
|
|
||||||
with:
|
|
||||||
name: query-pack-zip
|
|
||||||
path: query-pack-zip
|
|
||||||
- uses: actions/download-artifact@v2
|
|
||||||
with:
|
|
||||||
name: extractor-ubuntu-latest
|
|
||||||
path: linux64
|
|
||||||
- run: |
|
|
||||||
unzip query-pack-zip/*.zip -d pack
|
|
||||||
cp -r ql/codeql-extractor.yml ql/tools ql/ql/src/ql.dbscheme.stats pack/
|
|
||||||
mkdir -p pack/tools/linux64
|
|
||||||
if [[ -f linux64/ql-autobuilder ]]; then
|
|
||||||
cp linux64/ql-autobuilder pack/tools/linux64/autobuilder
|
|
||||||
chmod +x pack/tools/linux64/autobuilder
|
|
||||||
fi
|
|
||||||
if [[ -f linux64/ql-extractor ]]; then
|
|
||||||
cp linux64/ql-extractor pack/tools/linux64/extractor
|
|
||||||
chmod +x pack/tools/linux64/extractor
|
|
||||||
fi
|
|
||||||
cd pack
|
|
||||||
zip -rq ../codeql-ql.zip .
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: codeql-ql-pack
|
|
||||||
path: codeql-ql.zip
|
|
||||||
retention-days: 1
|
|
||||||
analyze:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
folder: [cpp, csharp, java, javascript, python, ql, ruby]
|
|
||||||
|
|
||||||
needs:
|
|
||||||
- package
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Download pack
|
|
||||||
uses: actions/download-artifact@v2
|
|
||||||
with:
|
|
||||||
name: codeql-ql-pack
|
|
||||||
path: ${{ runner.temp }}/codeql-ql-pack-artifact
|
|
||||||
|
|
||||||
- name: Prepare pack
|
|
||||||
run: |
|
|
||||||
unzip "${PACK_ARTIFACT}/*.zip" -d "${PACK}"
|
|
||||||
env:
|
|
||||||
PACK_ARTIFACT: ${{ runner.temp }}/codeql-ql-pack-artifact
|
|
||||||
PACK: ${{ runner.temp }}/pack
|
|
||||||
- name: Hack codeql-action options
|
|
||||||
run: |
|
|
||||||
JSON=$(jq -nc --arg pack "${PACK}" '.resolve.queries=["--search-path", $pack] | .resolve.extractor=["--search-path", $pack] | .database.init=["--search-path", $pack]')
|
|
||||||
echo "CODEQL_ACTION_EXTRA_OPTIONS=${JSON}" >> ${GITHUB_ENV}
|
|
||||||
env:
|
|
||||||
PACK: ${{ runner.temp }}/pack
|
|
||||||
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Create CodeQL config file
|
|
||||||
run: |
|
|
||||||
echo "paths:" > ${CONF}
|
|
||||||
echo " - ${FOLDER}" >> ${CONF}
|
|
||||||
echo "paths-ignore:" >> ${CONF}
|
|
||||||
echo " - ql/ql/test" >> ${CONF}
|
|
||||||
echo "Config file: "
|
|
||||||
cat ${CONF}
|
|
||||||
env:
|
|
||||||
CONF: ./ql-for-ql-config.yml
|
|
||||||
FOLDER: ${{ matrix.folder }}
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@erik-krogh/ql
|
|
||||||
with:
|
|
||||||
languages: ql
|
|
||||||
db-location: ${{ runner.temp }}/db
|
|
||||||
config-file: ./ql-for-ql-config.yml
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@erik-krogh/ql
|
|
||||||
with:
|
|
||||||
category: "ql-for-ql-${{ matrix.folder }}"
|
|
||||||
|
|
||||||
84
.github/workflows/ql-for-ql-dataset_measure.yml
vendored
84
.github/workflows/ql-for-ql-dataset_measure.yml
vendored
@@ -1,84 +0,0 @@
|
|||||||
name: Collect database stats for QL for QL
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
paths:
|
|
||||||
- ql/ql/src/ql.dbscheme
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
paths:
|
|
||||||
- ql/ql/src/ql.dbscheme
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
measure:
|
|
||||||
env:
|
|
||||||
CODEQL_THREADS: 4 # TODO: remove this once it's set by the CLI
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
repo:
|
|
||||||
- github/codeql
|
|
||||||
- github/codeql-go
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Find codeql
|
|
||||||
id: find-codeql
|
|
||||||
uses: github/codeql-action/init@erik-krogh/ql
|
|
||||||
with:
|
|
||||||
languages: javascript # does not matter
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry
|
|
||||||
~/.cargo/git
|
|
||||||
ql/target
|
|
||||||
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
- name: Build Extractor
|
|
||||||
run: cd ql; env "PATH=$PATH:`dirname ${CODEQL}`" ./create-extractor-pack.sh
|
|
||||||
env:
|
|
||||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
|
||||||
- name: Checkout ${{ matrix.repo }}
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
repository: ${{ matrix.repo }}
|
|
||||||
path: ${{ github.workspace }}/repo
|
|
||||||
- name: Create database
|
|
||||||
run: |
|
|
||||||
"${CODEQL}" database create \
|
|
||||||
--search-path "ql/extractor-pack" \
|
|
||||||
--threads 4 \
|
|
||||||
--language ql --source-root "${{ github.workspace }}/repo" \
|
|
||||||
"${{ runner.temp }}/database"
|
|
||||||
env:
|
|
||||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
|
||||||
- name: Measure database
|
|
||||||
run: |
|
|
||||||
mkdir -p "stats/${{ matrix.repo }}"
|
|
||||||
"${CODEQL}" dataset measure --threads 4 --output "stats/${{ matrix.repo }}/stats.xml" "${{ runner.temp }}/database/db-ql"
|
|
||||||
env:
|
|
||||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: measurements
|
|
||||||
path: stats
|
|
||||||
retention-days: 1
|
|
||||||
|
|
||||||
merge:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: measure
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- uses: actions/download-artifact@v2
|
|
||||||
with:
|
|
||||||
name: measurements
|
|
||||||
path: stats
|
|
||||||
- run: |
|
|
||||||
python -m pip install --user lxml
|
|
||||||
find stats -name 'stats.xml' -print0 | sort -z | xargs -0 python ql/scripts/merge_stats.py --output ql/ql/src/ql.dbscheme.stats --normalise ql_tokeninfo
|
|
||||||
- uses: actions/upload-artifact@v2
|
|
||||||
with:
|
|
||||||
name: ql.dbscheme.stats
|
|
||||||
path: ql/ql/src/ql.dbscheme.stats
|
|
||||||
52
.github/workflows/ql-for-ql-tests.yml
vendored
52
.github/workflows/ql-for-ql-tests.yml
vendored
@@ -1,52 +0,0 @@
|
|||||||
name: Run QL for QL Tests
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
paths:
|
|
||||||
- "ql/**"
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
paths:
|
|
||||||
- "ql/**"
|
|
||||||
|
|
||||||
env:
|
|
||||||
CARGO_TERM_COLOR: always
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
qltest:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Find codeql
|
|
||||||
id: find-codeql
|
|
||||||
uses: github/codeql-action/init@erik-krogh/ql
|
|
||||||
with:
|
|
||||||
languages: javascript # does not matter
|
|
||||||
- uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry
|
|
||||||
~/.cargo/git
|
|
||||||
ql/target
|
|
||||||
key: ${{ runner.os }}-qltest-cargo-${{ hashFiles('**/Cargo.lock') }}
|
|
||||||
- name: Build extractor
|
|
||||||
run: |
|
|
||||||
cd ql;
|
|
||||||
codeqlpath=$(dirname ${{ steps.find-codeql.outputs.codeql-path }});
|
|
||||||
env "PATH=$PATH:$codeqlpath" ./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
|
|
||||||
env:
|
|
||||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
|
||||||
- name: Check QL formatting
|
|
||||||
run: |
|
|
||||||
find ql/ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 "${CODEQL}" query format --check-only
|
|
||||||
env:
|
|
||||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
|
||||||
- name: Check QL compilation
|
|
||||||
run: |
|
|
||||||
"${CODEQL}" query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ql/extractor-pack" "ql/ql/src" "ql/ql/examples"
|
|
||||||
env:
|
|
||||||
CODEQL: ${{ steps.find-codeql.outputs.codeql-path }}
|
|
||||||
20
.github/workflows/ruby-build.yml
vendored
20
.github/workflows/ruby-build.yml
vendored
@@ -3,18 +3,16 @@ name: "Ruby: Build"
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- "ruby/**"
|
- 'ruby/**'
|
||||||
- .github/workflows/ruby-build.yml
|
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- "rc/*"
|
- 'rc/*'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- "ruby/**"
|
- 'ruby/**'
|
||||||
- .github/workflows/ruby-build.yml
|
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- "rc/*"
|
- 'rc/*'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
tag:
|
tag:
|
||||||
@@ -102,6 +100,16 @@ jobs:
|
|||||||
PACK_FOLDER=$(readlink -f target/packs/codeql/ruby-queries/*)
|
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/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}/{}" \;)
|
(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}/{}" \;)
|
||||||
|
- name: Compile with previous CodeQL versions
|
||||||
|
run: |
|
||||||
|
for version in $(gh release list --repo https://github.com/github/codeql-cli-binaries | cut -f 1 | sort --version-sort | tail -3 | head -2); do
|
||||||
|
rm -f codeql-linux64.zip
|
||||||
|
gh release download --repo https://github.com/github/codeql-cli-binaries --pattern codeql-linux64.zip "$version"
|
||||||
|
rm -rf codeql; unzip -q codeql-linux64.zip
|
||||||
|
codeql/codeql query compile target/packs/*
|
||||||
|
done
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
- uses: actions/upload-artifact@v2
|
- uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
name: codeql-ruby-queries
|
name: codeql-ruby-queries
|
||||||
|
|||||||
10
.github/workflows/ruby-dataset-measure.yml
vendored
10
.github/workflows/ruby-dataset-measure.yml
vendored
@@ -4,17 +4,15 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- "rc/*"
|
- 'rc/*'
|
||||||
paths:
|
paths:
|
||||||
- ruby/ql/lib/ruby.dbscheme
|
- ruby/ql/lib/ruby.dbscheme
|
||||||
- .github/workflows/ruby-dataset-measure.yml
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- "rc/*"
|
- 'rc/*'
|
||||||
paths:
|
paths:
|
||||||
- ruby/ql/lib/ruby.dbscheme
|
- ruby/ql/lib/ruby.dbscheme
|
||||||
- .github/workflows/ruby-dataset-measure.yml
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -24,7 +22,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
repo: [rails/rails, discourse/discourse, spree/spree, ruby/ruby]
|
repo: [rails/rails, discourse/discourse, spree/spree]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -41,7 +39,7 @@ jobs:
|
|||||||
- name: Create database
|
- name: Create database
|
||||||
run: |
|
run: |
|
||||||
codeql database create \
|
codeql database create \
|
||||||
--search-path "${{ github.workspace }}/ruby/extractor-pack" \
|
--search-path "${{ github.workspace }}/ruby" \
|
||||||
--threads 4 \
|
--threads 4 \
|
||||||
--language ruby --source-root "${{ github.workspace }}/repo" \
|
--language ruby --source-root "${{ github.workspace }}/repo" \
|
||||||
"${{ runner.temp }}/database"
|
"${{ runner.temp }}/database"
|
||||||
|
|||||||
16
.github/workflows/ruby-qltest.yml
vendored
16
.github/workflows/ruby-qltest.yml
vendored
@@ -3,18 +3,16 @@ name: "Ruby: Run QL Tests"
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- "ruby/**"
|
- 'ruby/**'
|
||||||
- .github/workflows/ruby-qltest.yml
|
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- "rc/*"
|
- 'rc/*'
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- "ruby/**"
|
- 'ruby/**'
|
||||||
- .github/workflows/ruby-qltest.yml
|
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- "rc/*"
|
- 'rc/*'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
@@ -32,19 +30,19 @@ jobs:
|
|||||||
- uses: ./ruby/actions/create-extractor-pack
|
- uses: ./ruby/actions/create-extractor-pack
|
||||||
- name: Run QL tests
|
- name: Run QL tests
|
||||||
run: |
|
run: |
|
||||||
codeql test run --search-path "${{ github.workspace }}/ruby/extractor-pack" --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --consistency-queries ql/consistency-queries ql/test
|
codeql test run --check-databases --check-unused-labels --check-repeated-labels --check-redefined-labels --check-use-before-definition --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}" --consistency-queries ql/consistency-queries ql/test
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
- name: Check QL formatting
|
- name: Check QL formatting
|
||||||
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
|
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
|
||||||
- name: Check QL compilation
|
- name: Check QL compilation
|
||||||
run: |
|
run: |
|
||||||
codeql query compile --check-only --threads=4 --warnings=error "ql/src" "ql/examples"
|
codeql query compile --check-only --threads=4 --warnings=error --search-path "${{ github.workspace }}/ruby" --additional-packs "${{ github.workspace }}" "ql/src" "ql/examples"
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ github.token }}
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
- name: Check DB upgrade scripts
|
- name: Check DB upgrade scripts
|
||||||
run: |
|
run: |
|
||||||
echo >empty.trap
|
echo >empty.trap
|
||||||
codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap
|
codeql dataset import -S ql/lib/upgrades/initial/ruby.dbscheme testdb empty.trap
|
||||||
codeql dataset upgrade testdb --additional-packs ql/lib
|
codeql dataset upgrade testdb --additional-packs ql/lib/upgrades
|
||||||
diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme
|
diff -q testdb/ruby.dbscheme ql/lib/ruby.dbscheme
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -27,6 +27,3 @@ csharp/extractor/Semmle.Extraction.CSharp.Driver/Properties/launchSettings.json
|
|||||||
|
|
||||||
# Avoid committing cached package components
|
# Avoid committing cached package components
|
||||||
.codeql
|
.codeql
|
||||||
|
|
||||||
# Compiled class file
|
|
||||||
*.class
|
|
||||||
@@ -25,6 +25,3 @@
|
|||||||
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
/docs/codeql-for-visual-studio-code/ @github/codeql-vscode-reviewers
|
||||||
/docs/ql-language-reference/ @github/codeql-frontend-reviewers
|
/docs/ql-language-reference/ @github/codeql-frontend-reviewers
|
||||||
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
|
/docs/query-*-style-guide.md @github/codeql-analysis-reviewers
|
||||||
|
|
||||||
# QL for QL reviewers
|
|
||||||
/ql/ @erik-krogh @tausbn
|
|
||||||
@@ -11,14 +11,13 @@ If you have an idea for a query that you would like to share with other CodeQL u
|
|||||||
|
|
||||||
1. **Directory structure**
|
1. **Directory structure**
|
||||||
|
|
||||||
There are six language-specific query directories in this repository:
|
There are five language-specific query directories in this repository:
|
||||||
|
|
||||||
* C/C++: `cpp/ql/src`
|
* C/C++: `cpp/ql/src`
|
||||||
* C#: `csharp/ql/src`
|
* C#: `csharp/ql/src`
|
||||||
* Java: `java/ql/src`
|
* Java: `java/ql/src`
|
||||||
* JavaScript: `javascript/ql/src`
|
* JavaScript: `javascript/ql/src`
|
||||||
* Python: `python/ql/src`
|
* Python: `python/ql/src`
|
||||||
* Ruby: `ruby/ql/src`
|
|
||||||
|
|
||||||
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
|
Each language-specific directory contains further subdirectories that group queries based on their `@tags` or purpose.
|
||||||
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
|
- Experimental queries and libraries are stored in the `experimental` subdirectory within each language-specific directory in the [CodeQL repository](https://github.com/github/codeql). For example, experimental Java queries and libraries are stored in `java/ql/src/experimental` and any corresponding tests in `java/ql/test/experimental`.
|
||||||
|
|||||||
@@ -449,27 +449,19 @@
|
|||||||
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
|
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/pressa/SsaImplCommon.qll",
|
||||||
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
|
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/basessa/SsaImplCommon.qll",
|
||||||
"csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll",
|
"csharp/ql/lib/semmle/code/cil/internal/SsaImplCommon.qll",
|
||||||
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll",
|
"ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImplCommon.qll"
|
||||||
"cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaImplCommon.qll"
|
|
||||||
],
|
],
|
||||||
"CryptoAlgorithms Python/JS/Ruby": [
|
"CryptoAlgorithms Python/JS": [
|
||||||
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
|
"javascript/ql/lib/semmle/javascript/security/CryptoAlgorithms.qll",
|
||||||
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll",
|
"python/ql/lib/semmle/python/concepts/CryptoAlgorithms.qll"
|
||||||
"ruby/ql/lib/codeql/ruby/security/CryptoAlgorithms.qll"
|
|
||||||
],
|
|
||||||
"CryptoAlgorithmNames Python/JS/Ruby": [
|
|
||||||
"javascript/ql/lib/semmle/javascript/security/internal/CryptoAlgorithmNames.qll",
|
|
||||||
"python/ql/lib/semmle/python/concepts/internal/CryptoAlgorithmNames.qll",
|
|
||||||
"ruby/ql/lib/codeql/ruby/security/internal/CryptoAlgorithmNames.qll"
|
|
||||||
],
|
],
|
||||||
"SensitiveDataHeuristics Python/JS": [
|
"SensitiveDataHeuristics Python/JS": [
|
||||||
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
|
"javascript/ql/lib/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
|
||||||
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll"
|
"python/ql/lib/semmle/python/security/internal/SensitiveDataHeuristics.qll"
|
||||||
],
|
],
|
||||||
"ReDoS Util Python/JS/Ruby": [
|
"ReDoS Util Python/JS": [
|
||||||
"javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll",
|
"javascript/ql/lib/semmle/javascript/security/performance/ReDoSUtil.qll",
|
||||||
"python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll",
|
"python/ql/lib/semmle/python/security/performance/ReDoSUtil.qll"
|
||||||
"ruby/ql/lib/codeql/ruby/security/performance/ReDoSUtil.qll"
|
|
||||||
],
|
],
|
||||||
"ReDoS Exponential Python/JS": [
|
"ReDoS Exponential Python/JS": [
|
||||||
"javascript/ql/lib/semmle/javascript/security/performance/ExponentialBackTracking.qll",
|
"javascript/ql/lib/semmle/javascript/security/performance/ExponentialBackTracking.qll",
|
||||||
@@ -478,12 +470,7 @@
|
|||||||
"ReDoS Polynomial Python/JS": [
|
"ReDoS Polynomial Python/JS": [
|
||||||
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
|
"javascript/ql/lib/semmle/javascript/security/performance/SuperlinearBackTracking.qll",
|
||||||
"python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll",
|
"python/ql/lib/semmle/python/security/performance/SuperlinearBackTracking.qll",
|
||||||
"ruby/ql/lib/codeql/ruby/security/performance/SuperlinearBackTracking.qll"
|
"ruby/ql/lib/codeql/ruby/regexp/SuperlinearBackTracking.qll"
|
||||||
],
|
|
||||||
"BadTagFilterQuery Python/JS/Ruby": [
|
|
||||||
"javascript/ql/lib/semmle/javascript/security/BadTagFilterQuery.qll",
|
|
||||||
"python/ql/lib/semmle/python/security/BadTagFilterQuery.qll",
|
|
||||||
"ruby/ql/lib/codeql/ruby/security/BadTagFilterQuery.qll"
|
|
||||||
],
|
],
|
||||||
"CFG": [
|
"CFG": [
|
||||||
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
|
"csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll",
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Build" Version="16.11.0" />
|
<PackageReference Include="Microsoft.Build" Version="16.9.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
lgtm,codescanning
|
|
||||||
* The "Cleartext transmission of sensitive information" (`cpp/cleartext-transmission`) query has been improved, reducing the number of false positive results when encryption is present.
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
## 0.0.7
|
|
||||||
|
|
||||||
## 0.0.6
|
|
||||||
|
|
||||||
## 0.0.5
|
|
||||||
|
|
||||||
## 0.0.4
|
|
||||||
|
|
||||||
### New Features
|
|
||||||
|
|
||||||
* The QL library `semmle.code.cpp.commons.Exclusions` now contains a predicate
|
|
||||||
`isFromSystemMacroDefinition` for identifying code that originates from a
|
|
||||||
macro outside the project being analyzed.
|
|
||||||
@@ -73,7 +73,7 @@ class Options extends string {
|
|||||||
* __assume(0);
|
* __assume(0);
|
||||||
* ```
|
* ```
|
||||||
* (note that in this case if the hint is wrong and the expression is reached at
|
* (note that in this case if the hint is wrong and the expression is reached at
|
||||||
* runtime, the program's behavior is undefined)
|
* runtime, the program's behaviour is undefined)
|
||||||
*/
|
*/
|
||||||
predicate exprExits(Expr e) {
|
predicate exprExits(Expr e) {
|
||||||
e.(AssumeExpr).getChild(0).(CompileTimeConstantInt).getIntValue() = 0 or
|
e.(AssumeExpr).getChild(0).(CompileTimeConstantInt).getIntValue() = 0 or
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class CustomOptions extends Options {
|
|||||||
* __assume(0);
|
* __assume(0);
|
||||||
* ```
|
* ```
|
||||||
* (note that in this case if the hint is wrong and the expression is reached at
|
* (note that in this case if the hint is wrong and the expression is reached at
|
||||||
* runtime, the program's behavior is undefined)
|
* runtime, the program's behaviour is undefined)
|
||||||
*/
|
*/
|
||||||
override predicate exprExits(Expr e) { Options.super.exprExits(e) }
|
override predicate exprExits(Expr e) { Options.super.exprExits(e) }
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
## 0.0.4
|
|
||||||
|
|
||||||
### New Features
|
|
||||||
|
|
||||||
* The QL library `semmle.code.cpp.commons.Exclusions` now contains a predicate
|
|
||||||
`isFromSystemMacroDefinition` for identifying code that originates from a
|
|
||||||
macro outside the project being analyzed.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
## 0.0.5
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
## 0.0.6
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
## 0.0.7
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
---
|
|
||||||
lastReleaseVersion: 0.0.7
|
|
||||||
@@ -37,7 +37,7 @@ abstract class SimpleRangeAnalysisDefinition extends RangeSsaDefinition {
|
|||||||
* dependencies. Without this information, range analysis might work for
|
* dependencies. Without this information, range analysis might work for
|
||||||
* simple cases but will go into infinite loops on complex code.
|
* simple cases but will go into infinite loops on complex code.
|
||||||
*
|
*
|
||||||
* For example, when modeling the definition by reference in a call to an
|
* For example, when modelling the definition by reference in a call to an
|
||||||
* overloaded `operator=`, written as `v = e`, the definition of `(this, v)`
|
* overloaded `operator=`, written as `v = e`, the definition of `(this, v)`
|
||||||
* depends on `e`.
|
* depends on `e`.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* `Instruction` level), and then using the array length analysis and the range
|
* `Instruction` level), and then using the array length analysis and the range
|
||||||
* analysis together to prove that some of these pointer dereferences are safe.
|
* analysis together to prove that some of these pointer dereferences are safe.
|
||||||
*
|
*
|
||||||
* The analysis is soundy, i.e. it is sound if no undefined behavior is present
|
* The analysis is soundy, i.e. it is sound if no undefined behaviour is present
|
||||||
* in the program.
|
* in the program.
|
||||||
* Furthermore, it crucially depends on the soundiness of the range analysis and
|
* Furthermore, it crucially depends on the soundiness of the range analysis and
|
||||||
* the array length analysis.
|
* the array length analysis.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
name: codeql/cpp-all
|
name: codeql/cpp-all
|
||||||
version: 0.0.7
|
version: 0.0.2
|
||||||
groups: cpp
|
|
||||||
dbscheme: semmlecode.cpp.dbscheme
|
dbscheme: semmlecode.cpp.dbscheme
|
||||||
extractor: cpp
|
extractor: cpp
|
||||||
library: true
|
library: true
|
||||||
dependencies:
|
dependencies:
|
||||||
codeql/cpp-upgrades: ^0.0.3
|
codeql/cpp-upgrades: 0.0.2
|
||||||
|
|||||||
@@ -237,7 +237,7 @@ class Class extends UserType {
|
|||||||
exists(ClassDerivation cd | cd.getBaseClass() = base |
|
exists(ClassDerivation cd | cd.getBaseClass() = base |
|
||||||
result =
|
result =
|
||||||
this.accessOfBaseMemberMulti(cd.getDerivedClass(),
|
this.accessOfBaseMemberMulti(cd.getDerivedClass(),
|
||||||
fieldInBase.accessInDirectDerived(cd.getASpecifier()))
|
fieldInBase.accessInDirectDerived(cd.getASpecifier().(AccessSpecifier)))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +261,8 @@ class Class extends UserType {
|
|||||||
* includes the case of `base` = `this`.
|
* includes the case of `base` = `this`.
|
||||||
*/
|
*/
|
||||||
AccessSpecifier accessOfBaseMember(Declaration member) {
|
AccessSpecifier accessOfBaseMember(Declaration member) {
|
||||||
result = this.accessOfBaseMember(member.getDeclaringType(), member.getASpecifier())
|
result =
|
||||||
|
this.accessOfBaseMember(member.getDeclaringType(), member.getASpecifier().(AccessSpecifier))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -318,7 +319,7 @@ class Class extends UserType {
|
|||||||
exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() |
|
exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() |
|
||||||
// Note: Overload resolution is not implemented -- all copy
|
// Note: Overload resolution is not implemented -- all copy
|
||||||
// constructors are considered equal.
|
// constructors are considered equal.
|
||||||
this.cannotAccessCopyConstructorOnAny(t)
|
this.cannotAccessCopyConstructorOnAny(t.(Class))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// - T has direct or virtual base class that cannot be copied (has deleted,
|
// - T has direct or virtual base class that cannot be copied (has deleted,
|
||||||
@@ -391,7 +392,7 @@ class Class extends UserType {
|
|||||||
exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() |
|
exists(Type t | t = this.getAFieldSubobjectType().getUnspecifiedType() |
|
||||||
// Note: Overload resolution is not implemented -- all copy assignment
|
// Note: Overload resolution is not implemented -- all copy assignment
|
||||||
// operators are considered equal.
|
// operators are considered equal.
|
||||||
this.cannotAccessCopyAssignmentOperatorOnAny(t)
|
this.cannotAccessCopyAssignmentOperatorOnAny(t.(Class))
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Class c | c = this.getADirectOrVirtualBase() |
|
exists(Class c | c = this.getADirectOrVirtualBase() |
|
||||||
|
|||||||
@@ -490,7 +490,8 @@ class AccessHolder extends Declaration, TAccessHolder {
|
|||||||
*/
|
*/
|
||||||
pragma[inline]
|
pragma[inline]
|
||||||
predicate canAccessMember(Declaration member, Class derived) {
|
predicate canAccessMember(Declaration member, Class derived) {
|
||||||
this.couldAccessMember(member.getDeclaringType(), member.getASpecifier(), derived)
|
this.couldAccessMember(member.getDeclaringType(), member.getASpecifier().(AccessSpecifier),
|
||||||
|
derived)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest)
|
|||||||
dependsOnTransitive(src, mid) and
|
dependsOnTransitive(src, mid) and
|
||||||
not mid instanceof Type and
|
not mid instanceof Type and
|
||||||
not mid instanceof EnumConstant and
|
not mid instanceof EnumConstant and
|
||||||
getDeclarationEntries(mid, dest) and
|
getDeclarationEntries(mid, dest.(DeclarationEntry)) and
|
||||||
not dest instanceof TypeDeclarationEntry
|
not dest instanceof TypeDeclarationEntry
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
@@ -283,9 +283,9 @@ private predicate dependsOnDeclarationEntry(Element src, DeclarationEntry dest)
|
|||||||
// dependency from a Type / Variable / Function use -> any (visible) definition
|
// dependency from a Type / Variable / Function use -> any (visible) definition
|
||||||
dependsOnTransitive(src, mid) and
|
dependsOnTransitive(src, mid) and
|
||||||
not mid instanceof EnumConstant and
|
not mid instanceof EnumConstant and
|
||||||
getDeclarationEntries(mid, dest) and
|
getDeclarationEntries(mid, dest.(DeclarationEntry)) and
|
||||||
// must be definition
|
// must be definition
|
||||||
dest.isDefinition()
|
dest.(DeclarationEntry).isDefinition()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,7 +307,7 @@ private predicate dependsOnFull(DependsSource src, Symbol dest, int category) {
|
|||||||
// dependency from a Variable / Function use -> non-visible definition (link time)
|
// dependency from a Variable / Function use -> non-visible definition (link time)
|
||||||
dependsOnTransitive(src, mid) and
|
dependsOnTransitive(src, mid) and
|
||||||
not mid instanceof EnumConstant and
|
not mid instanceof EnumConstant and
|
||||||
getDeclarationEntries(mid, dest) and
|
getDeclarationEntries(mid, dest.(DeclarationEntry)) and
|
||||||
not dest instanceof TypeDeclarationEntry and
|
not dest instanceof TypeDeclarationEntry and
|
||||||
// must be definition
|
// must be definition
|
||||||
dest.(DeclarationEntry).isDefinition() and
|
dest.(DeclarationEntry).isDefinition() and
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ predicate functionContainsPreprocCode(Function f) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `e` is completely or partially from a macro invocation `mi`, as
|
* Holds if `e` is completely or partially from a macro definition, as opposed
|
||||||
* opposed to being passed in as an argument.
|
* to being passed in as an argument.
|
||||||
*
|
*
|
||||||
* In the following example, the call to `f` is from a macro definition,
|
* In the following example, the call to `f` is from a macro definition,
|
||||||
* while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
|
* while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
|
||||||
@@ -93,8 +93,8 @@ predicate functionContainsPreprocCode(Function f) {
|
|||||||
* M(y + 1);
|
* M(y + 1);
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
private predicate isFromMacroInvocation(Element e, MacroInvocation mi) {
|
predicate isFromMacroDefinition(Element e) {
|
||||||
exists(Location eLocation, Location miLocation |
|
exists(MacroInvocation mi, Location eLocation, Location miLocation |
|
||||||
mi.getAnExpandedElement() = e and
|
mi.getAnExpandedElement() = e and
|
||||||
eLocation = e.getLocation() and
|
eLocation = e.getLocation() and
|
||||||
miLocation = mi.getLocation() and
|
miLocation = mi.getLocation() and
|
||||||
@@ -109,36 +109,3 @@ private predicate isFromMacroInvocation(Element e, MacroInvocation mi) {
|
|||||||
eLocation.getEndColumn() >= miLocation.getEndColumn()
|
eLocation.getEndColumn() >= miLocation.getEndColumn()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `e` is completely or partially from a macro definition, as opposed
|
|
||||||
* to being passed in as an argument.
|
|
||||||
*
|
|
||||||
* In the following example, the call to `f` is from a macro definition,
|
|
||||||
* while `y`, `+`, `1`, and `;` are not. This assumes that no identifier apart
|
|
||||||
* from `M` refers to a macro.
|
|
||||||
* ```
|
|
||||||
* #define M(x) f(x)
|
|
||||||
* ...
|
|
||||||
* M(y + 1);
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
predicate isFromMacroDefinition(Element e) { isFromMacroInvocation(e, _) }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `e` is completely or partially from a _system macro_ definition, as
|
|
||||||
* opposed to being passed in as an argument. A system macro is a macro whose
|
|
||||||
* definition is outside the source directory of the database.
|
|
||||||
*
|
|
||||||
* If the system macro is invoked through a non-system macro, then this
|
|
||||||
* predicate does not hold.
|
|
||||||
*
|
|
||||||
* See also `isFromMacroDefinition`.
|
|
||||||
*/
|
|
||||||
predicate isFromSystemMacroDefinition(Element e) {
|
|
||||||
exists(MacroInvocation mi |
|
|
||||||
isFromMacroInvocation(e, mi) and
|
|
||||||
// Has no relative path in the database, meaning it's a system file.
|
|
||||||
not exists(mi.getMacro().getFile().getRelativePath())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -3,33 +3,17 @@ private import semmle.code.cpp.models.interfaces.ArrayFunction
|
|||||||
private import semmle.code.cpp.models.implementations.Strcat
|
private import semmle.code.cpp.models.implementations.Strcat
|
||||||
import semmle.code.cpp.dataflow.DataFlow
|
import semmle.code.cpp.dataflow.DataFlow
|
||||||
|
|
||||||
/**
|
private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, Expr e0) {
|
||||||
* Holds if the expression `e` assigns something including `va` to a
|
exists(StackVariable v0, Expr val |
|
||||||
* stack variable `v0`.
|
exprDefinition(v0, e, val) and
|
||||||
*/
|
val.getAChild*() = va and
|
||||||
private predicate mayAddNullTerminatorHelper(Expr e, VariableAccess va, StackVariable v0) {
|
mayAddNullTerminator(e0, v0.getAnAccess())
|
||||||
exists(Expr val |
|
|
||||||
exprDefinition(v0, e, val) and // `e` is `v0 := val`
|
|
||||||
val.getAChild*() = va
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
bindingset[n1, n2]
|
|
||||||
private predicate controlFlowNodeSuccessorTransitive(ControlFlowNode n1, ControlFlowNode n2) {
|
|
||||||
exists(BasicBlock bb1, int pos1, BasicBlock bb2, int pos2 |
|
|
||||||
pragma[only_bind_into](bb1).getNode(pos1) = n1 and
|
|
||||||
pragma[only_bind_into](bb2).getNode(pos2) = n2 and
|
|
||||||
(
|
|
||||||
bb1 = bb2 and pos1 < pos2
|
|
||||||
or
|
|
||||||
bb1.getASuccessor+() = bb2
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if the expression `e` may add a null terminator to the string
|
* Holds if the expression `e` may add a null terminator to the string in
|
||||||
* accessed by `va`.
|
* variable `v`.
|
||||||
*/
|
*/
|
||||||
predicate mayAddNullTerminator(Expr e, VariableAccess va) {
|
predicate mayAddNullTerminator(Expr e, VariableAccess va) {
|
||||||
// Assignment: dereferencing or array access
|
// Assignment: dereferencing or array access
|
||||||
@@ -46,10 +30,14 @@ predicate mayAddNullTerminator(Expr e, VariableAccess va) {
|
|||||||
)
|
)
|
||||||
or
|
or
|
||||||
// Assignment to another stack variable
|
// Assignment to another stack variable
|
||||||
exists(StackVariable v0, Expr e0 |
|
exists(Expr e0, BasicBlock bb, int pos, BasicBlock bb0, int pos0 |
|
||||||
mayAddNullTerminatorHelper(e, va, v0) and
|
mayAddNullTerminatorHelper(e, va, e0) and
|
||||||
mayAddNullTerminator(pragma[only_bind_into](e0), pragma[only_bind_into](v0.getAnAccess())) and
|
bb.getNode(pos) = e and
|
||||||
controlFlowNodeSuccessorTransitive(e, e0)
|
bb0.getNode(pos0) = e0
|
||||||
|
|
|
||||||
|
bb = bb0 and pos < pos0
|
||||||
|
or
|
||||||
|
bb.getASuccessor+() = bb0
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// Assignment to non-stack variable
|
// Assignment to non-stack variable
|
||||||
@@ -131,9 +119,14 @@ predicate variableMustBeNullTerminated(VariableAccess va) {
|
|||||||
variableMustBeNullTerminated(use) and
|
variableMustBeNullTerminated(use) and
|
||||||
// Simplified: check that `p` may not be null terminated on *any*
|
// Simplified: check that `p` may not be null terminated on *any*
|
||||||
// path to `use` (including the one found via `parameterUsePair`)
|
// path to `use` (including the one found via `parameterUsePair`)
|
||||||
not exists(Expr e |
|
not exists(Expr e, BasicBlock bb1, int pos1, BasicBlock bb2, int pos2 |
|
||||||
mayAddNullTerminator(pragma[only_bind_into](e), p.getAnAccess()) and
|
mayAddNullTerminator(e, p.getAnAccess()) and
|
||||||
controlFlowNodeSuccessorTransitive(e, use)
|
bb1.getNode(pos1) = e and
|
||||||
|
bb2.getNode(pos2) = use
|
||||||
|
|
|
||||||
|
bb1 = bb2 and pos1 < pos2
|
||||||
|
or
|
||||||
|
bb1.getASuccessor+() = bb2
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,85 +6,6 @@ import semmle.code.cpp.Type
|
|||||||
import semmle.code.cpp.commons.CommonType
|
import semmle.code.cpp.commons.CommonType
|
||||||
import semmle.code.cpp.commons.StringAnalysis
|
import semmle.code.cpp.commons.StringAnalysis
|
||||||
import semmle.code.cpp.models.interfaces.FormattingFunction
|
import semmle.code.cpp.models.interfaces.FormattingFunction
|
||||||
private import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
|
|
||||||
private import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils
|
|
||||||
|
|
||||||
private newtype TBufferWriteEstimationReason =
|
|
||||||
TNoSpecifiedEstimateReason() or
|
|
||||||
TTypeBoundsAnalysis() or
|
|
||||||
TValueFlowAnalysis()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A reason for a specific buffer write size estimate.
|
|
||||||
*/
|
|
||||||
abstract class BufferWriteEstimationReason extends TBufferWriteEstimationReason {
|
|
||||||
/**
|
|
||||||
* Returns the name of the concrete class.
|
|
||||||
*/
|
|
||||||
abstract string toString();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a human readable representation of this reason.
|
|
||||||
*/
|
|
||||||
abstract string getDescription();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Combine estimate reasons. Used to give a reason for the size of a format string
|
|
||||||
* conversion given reasons coming from its individual specifiers.
|
|
||||||
*/
|
|
||||||
abstract BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* No particular reason given. This is currently used for backward compatibility so that
|
|
||||||
* classes derived from BufferWrite and overriding `getMaxData/0` still work with the
|
|
||||||
* queries as intended.
|
|
||||||
*/
|
|
||||||
class NoSpecifiedEstimateReason extends BufferWriteEstimationReason, TNoSpecifiedEstimateReason {
|
|
||||||
override string toString() { result = "NoSpecifiedEstimateReason" }
|
|
||||||
|
|
||||||
override string getDescription() { result = "no reason specified" }
|
|
||||||
|
|
||||||
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
|
|
||||||
// this reason should not be used in format specifiers, so it should not be combined
|
|
||||||
// with other reasons
|
|
||||||
none()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The estimation comes from rough bounds just based on the type (e.g.
|
|
||||||
* `0 <= x < 2^32` for an unsigned 32 bit integer).
|
|
||||||
*/
|
|
||||||
class TypeBoundsAnalysis extends BufferWriteEstimationReason, TTypeBoundsAnalysis {
|
|
||||||
override string toString() { result = "TypeBoundsAnalysis" }
|
|
||||||
|
|
||||||
override string getDescription() { result = "based on type bounds" }
|
|
||||||
|
|
||||||
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
|
|
||||||
other != TNoSpecifiedEstimateReason() and result = TTypeBoundsAnalysis()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The estimation comes from non trivial bounds found via actual flow analysis.
|
|
||||||
* For example
|
|
||||||
* ```
|
|
||||||
* unsigned u = x;
|
|
||||||
* if (u < 1000) {
|
|
||||||
* //... <- estimation done here based on u
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
class ValueFlowAnalysis extends BufferWriteEstimationReason, TValueFlowAnalysis {
|
|
||||||
override string toString() { result = "ValueFlowAnalysis" }
|
|
||||||
|
|
||||||
override string getDescription() { result = "based on flow analysis of value bounds" }
|
|
||||||
|
|
||||||
override BufferWriteEstimationReason combineWith(BufferWriteEstimationReason other) {
|
|
||||||
other != TNoSpecifiedEstimateReason() and result = other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PrintfFormatAttribute extends FormatAttribute {
|
class PrintfFormatAttribute extends FormatAttribute {
|
||||||
PrintfFormatAttribute() { this.getArchetype() = ["printf", "__printf__"] }
|
PrintfFormatAttribute() { this.getArchetype() = ["printf", "__printf__"] }
|
||||||
@@ -254,7 +175,9 @@ class FormattingFunctionCall extends Expr {
|
|||||||
/**
|
/**
|
||||||
* Gets the index at which the format string occurs in the argument list.
|
* Gets the index at which the format string occurs in the argument list.
|
||||||
*/
|
*/
|
||||||
int getFormatParameterIndex() { result = this.getTarget().getFormatParameterIndex() }
|
int getFormatParameterIndex() {
|
||||||
|
result = this.getTarget().(FormattingFunction).getFormatParameterIndex()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the format expression used in this call.
|
* Gets the format expression used in this call.
|
||||||
@@ -268,7 +191,7 @@ class FormattingFunctionCall extends Expr {
|
|||||||
exists(int i |
|
exists(int i |
|
||||||
result = this.getArgument(i) and
|
result = this.getArgument(i) and
|
||||||
n >= 0 and
|
n >= 0 and
|
||||||
n = i - this.getTarget().getFirstFormatArgumentIndex()
|
n = i - this.getTarget().(FormattingFunction).getFirstFormatArgumentIndex()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -328,7 +251,7 @@ class FormattingFunctionCall extends Expr {
|
|||||||
int getNumFormatArgument() {
|
int getNumFormatArgument() {
|
||||||
result = count(this.getFormatArgument(_)) and
|
result = count(this.getFormatArgument(_)) and
|
||||||
// format arguments must be known
|
// format arguments must be known
|
||||||
exists(this.getTarget().getFirstFormatArgumentIndex())
|
exists(this.getTarget().(FormattingFunction).getFirstFormatArgumentIndex())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -347,18 +270,6 @@ class FormattingFunctionCall extends Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the number of digits required to represent the integer represented by `f`.
|
|
||||||
*
|
|
||||||
* `f` is assumed to be nonnegative.
|
|
||||||
*/
|
|
||||||
bindingset[f]
|
|
||||||
private int lengthInBase10(float f) {
|
|
||||||
f = 0 and result = 1
|
|
||||||
or
|
|
||||||
result = f.log10().floor() + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to represent format strings that occur as arguments to invocations of formatting functions.
|
* A class to represent format strings that occur as arguments to invocations of formatting functions.
|
||||||
*/
|
*/
|
||||||
@@ -378,27 +289,35 @@ class FormatLiteral extends Literal {
|
|||||||
* a `char *` (either way, `%S` will have the opposite meaning).
|
* a `char *` (either way, `%S` will have the opposite meaning).
|
||||||
* DEPRECATED: Use getDefaultCharType() instead.
|
* DEPRECATED: Use getDefaultCharType() instead.
|
||||||
*/
|
*/
|
||||||
deprecated predicate isWideCharDefault() { this.getUse().getTarget().isWideCharDefault() }
|
deprecated predicate isWideCharDefault() {
|
||||||
|
this.getUse().getTarget().(FormattingFunction).isWideCharDefault()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the default character type expected for `%s` by this format literal. Typically
|
* Gets the default character type expected for `%s` by this format literal. Typically
|
||||||
* `char` or `wchar_t`.
|
* `char` or `wchar_t`.
|
||||||
*/
|
*/
|
||||||
Type getDefaultCharType() { result = this.getUse().getTarget().getDefaultCharType() }
|
Type getDefaultCharType() {
|
||||||
|
result = this.getUse().getTarget().(FormattingFunction).getDefaultCharType()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the non-default character type expected for `%S` by this format literal. Typically
|
* Gets the non-default character type expected for `%S` by this format literal. Typically
|
||||||
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
|
* `wchar_t` or `char`. On some snapshots there may be multiple results where we can't tell
|
||||||
* which is correct for a particular function.
|
* which is correct for a particular function.
|
||||||
*/
|
*/
|
||||||
Type getNonDefaultCharType() { result = this.getUse().getTarget().getNonDefaultCharType() }
|
Type getNonDefaultCharType() {
|
||||||
|
result = this.getUse().getTarget().(FormattingFunction).getNonDefaultCharType()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the wide character type for this format literal. This is usually `wchar_t`. On some
|
* Gets the wide character type for this format literal. This is usually `wchar_t`. On some
|
||||||
* snapshots there may be multiple results where we can't tell which is correct for a
|
* snapshots there may be multiple results where we can't tell which is correct for a
|
||||||
* particular function.
|
* particular function.
|
||||||
*/
|
*/
|
||||||
Type getWideCharType() { result = this.getUse().getTarget().getWideCharType() }
|
Type getWideCharType() {
|
||||||
|
result = this.getUse().getTarget().(FormattingFunction).getWideCharType()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this `FormatLiteral` is in a context that supports
|
* Holds if this `FormatLiteral` is in a context that supports
|
||||||
@@ -977,7 +896,7 @@ class FormatLiteral extends Literal {
|
|||||||
exists(string len, string conv |
|
exists(string len, string conv |
|
||||||
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
|
this.parseConvSpec(n, _, _, _, _, _, len, conv) and
|
||||||
(len != "l" and len != "w" and len != "h") and
|
(len != "l" and len != "w" and len != "h") and
|
||||||
this.getUse().getTarget().getFormatCharType().getSize() > 1 and // wide function
|
this.getUse().getTarget().(FormattingFunction).getFormatCharType().getSize() > 1 and // wide function
|
||||||
(
|
(
|
||||||
conv = "c" and
|
conv = "c" and
|
||||||
result = this.getNonDefaultCharType()
|
result = this.getNonDefaultCharType()
|
||||||
@@ -1067,14 +986,7 @@ class FormatLiteral extends Literal {
|
|||||||
* conversion specifier of this format string; has no result if this cannot
|
* conversion specifier of this format string; has no result if this cannot
|
||||||
* be determined.
|
* be determined.
|
||||||
*/
|
*/
|
||||||
int getMaxConvertedLength(int n) { result = max(getMaxConvertedLength(n, _)) }
|
int getMaxConvertedLength(int n) {
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the maximum length of the string that can be produced by the nth
|
|
||||||
* conversion specifier of this format string, specifying the estimation reason;
|
|
||||||
* has no result if this cannot be determined.
|
|
||||||
*/
|
|
||||||
int getMaxConvertedLength(int n, BufferWriteEstimationReason reason) {
|
|
||||||
exists(int len |
|
exists(int len |
|
||||||
(
|
(
|
||||||
(
|
(
|
||||||
@@ -1086,12 +998,10 @@ class FormatLiteral extends Literal {
|
|||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
this.getConversionChar(n) = "%" and
|
this.getConversionChar(n) = "%" and
|
||||||
len = 1 and
|
len = 1
|
||||||
reason = TValueFlowAnalysis()
|
|
||||||
or
|
or
|
||||||
this.getConversionChar(n).toLowerCase() = "c" and
|
this.getConversionChar(n).toLowerCase() = "c" and
|
||||||
len = 1 and
|
len = 1 // e.g. 'a'
|
||||||
reason = TValueFlowAnalysis() // e.g. 'a'
|
|
||||||
or
|
or
|
||||||
this.getConversionChar(n).toLowerCase() = "f" and
|
this.getConversionChar(n).toLowerCase() = "f" and
|
||||||
exists(int dot, int afterdot |
|
exists(int dot, int afterdot |
|
||||||
@@ -1105,8 +1015,7 @@ class FormatLiteral extends Literal {
|
|||||||
afterdot = 6
|
afterdot = 6
|
||||||
) and
|
) and
|
||||||
len = 1 + 309 + dot + afterdot
|
len = 1 + 309 + dot + afterdot
|
||||||
) and
|
) // e.g. -1e308="-100000"...
|
||||||
reason = TTypeBoundsAnalysis() // e.g. -1e308="-100000"...
|
|
||||||
or
|
or
|
||||||
this.getConversionChar(n).toLowerCase() = "e" and
|
this.getConversionChar(n).toLowerCase() = "e" and
|
||||||
exists(int dot, int afterdot |
|
exists(int dot, int afterdot |
|
||||||
@@ -1120,8 +1029,7 @@ class FormatLiteral extends Literal {
|
|||||||
afterdot = 6
|
afterdot = 6
|
||||||
) and
|
) and
|
||||||
len = 1 + 1 + dot + afterdot + 1 + 1 + 3
|
len = 1 + 1 + dot + afterdot + 1 + 1 + 3
|
||||||
) and
|
) // -1e308="-1.000000e+308"
|
||||||
reason = TTypeBoundsAnalysis() // -1e308="-1.000000e+308"
|
|
||||||
or
|
or
|
||||||
this.getConversionChar(n).toLowerCase() = "g" and
|
this.getConversionChar(n).toLowerCase() = "g" and
|
||||||
exists(int dot, int afterdot |
|
exists(int dot, int afterdot |
|
||||||
@@ -1144,79 +1052,42 @@ class FormatLiteral extends Literal {
|
|||||||
// (e.g. 123456, 0.000123456 are just OK)
|
// (e.g. 123456, 0.000123456 are just OK)
|
||||||
// so case %f can be at most P characters + 4 zeroes, sign, dot = P + 6
|
// so case %f can be at most P characters + 4 zeroes, sign, dot = P + 6
|
||||||
len = (afterdot.maximum(1) + 6).maximum(1 + 1 + dot + afterdot + 1 + 1 + 3)
|
len = (afterdot.maximum(1) + 6).maximum(1 + 1 + dot + afterdot + 1 + 1 + 3)
|
||||||
) and
|
) // (e.g. "-1.59203e-319")
|
||||||
reason = TTypeBoundsAnalysis() // (e.g. "-1.59203e-319")
|
|
||||||
or
|
or
|
||||||
this.getConversionChar(n).toLowerCase() = ["d", "i"] and
|
this.getConversionChar(n).toLowerCase() = ["d", "i"] and
|
||||||
// e.g. -2^31 = "-2147483648"
|
// e.g. -2^31 = "-2147483648"
|
||||||
exists(float typeBasedBound, float valueBasedBound |
|
exists(int sizeBits |
|
||||||
// The first case handles length sub-specifiers
|
sizeBits =
|
||||||
// Subtract one in the exponent because one bit is for the sign.
|
min(int bits |
|
||||||
// Add 1 to account for the possible sign in the output.
|
bits = this.getIntegralDisplayType(n).getSize() * 8
|
||||||
typeBasedBound =
|
|
||||||
1 + lengthInBase10(2.pow(this.getIntegralDisplayType(n).getSize() * 8 - 1)) and
|
|
||||||
// The second case uses range analysis to deduce a length that's shorter than the length
|
|
||||||
// of the number -2^31.
|
|
||||||
exists(Expr arg, float lower, float upper, float typeLower, float typeUpper |
|
|
||||||
arg = this.getUse().getConversionArgument(n) and
|
|
||||||
lower = lowerBound(arg.getFullyConverted()) and
|
|
||||||
upper = upperBound(arg.getFullyConverted()) and
|
|
||||||
typeLower = exprMinVal(arg.getFullyConverted()) and
|
|
||||||
typeUpper = exprMaxVal(arg.getFullyConverted())
|
|
||||||
|
|
|
||||||
valueBasedBound =
|
|
||||||
max(int cand |
|
|
||||||
// Include the sign bit in the length if it can be negative
|
|
||||||
(
|
|
||||||
if lower < 0
|
|
||||||
then cand = 1 + lengthInBase10(lower.abs())
|
|
||||||
else cand = lengthInBase10(lower)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
(
|
exists(IntegralType t |
|
||||||
if upper < 0
|
t = this.getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||||
then cand = 1 + lengthInBase10(upper.abs())
|
|
|
||||||
else cand = lengthInBase10(upper)
|
t.isSigned() and bits = t.getSize() * 8
|
||||||
)
|
)
|
||||||
) and
|
) and
|
||||||
(
|
len = 1 + ((sizeBits - 1) / 10.0.log2()).ceil()
|
||||||
if lower > typeLower or upper < typeUpper
|
// this calculation is as %u (below) only we take out the sign bit (- 1) and allow a whole
|
||||||
then reason = TValueFlowAnalysis()
|
// character for it to be expressed as '-'.
|
||||||
else reason = TTypeBoundsAnalysis()
|
|
||||||
)
|
|
||||||
) and
|
|
||||||
len = valueBasedBound.minimum(typeBasedBound)
|
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
this.getConversionChar(n).toLowerCase() = "u" and
|
this.getConversionChar(n).toLowerCase() = "u" and
|
||||||
// e.g. 2^32 - 1 = "4294967295"
|
// e.g. 2^32 - 1 = "4294967295"
|
||||||
exists(float typeBasedBound, float valueBasedBound |
|
exists(int sizeBits |
|
||||||
// The first case handles length sub-specifiers
|
sizeBits =
|
||||||
typeBasedBound = lengthInBase10(2.pow(this.getIntegralDisplayType(n).getSize() * 8) - 1) and
|
min(int bits |
|
||||||
// The second case uses range analysis to deduce a length that's shorter than
|
bits = this.getIntegralDisplayType(n).getSize() * 8
|
||||||
// the length of the number 2^31 - 1.
|
|
||||||
exists(Expr arg, float lower, float upper, float typeLower, float typeUpper |
|
|
||||||
arg = this.getUse().getConversionArgument(n) and
|
|
||||||
lower = lowerBound(arg.getFullyConverted()) and
|
|
||||||
upper = upperBound(arg.getFullyConverted()) and
|
|
||||||
typeLower = exprMinVal(arg.getFullyConverted()) and
|
|
||||||
typeUpper = exprMaxVal(arg.getFullyConverted())
|
|
||||||
|
|
|
||||||
valueBasedBound =
|
|
||||||
lengthInBase10(max(float cand |
|
|
||||||
// If lower can be negative we use `(unsigned)-1` as the candidate value.
|
|
||||||
lower < 0 and
|
|
||||||
cand = 2.pow(any(IntType t | t.isUnsigned()).getSize() * 8)
|
|
||||||
or
|
or
|
||||||
cand = upper
|
exists(IntegralType t |
|
||||||
)) and
|
t = this.getUse().getConversionArgument(n).getType().getUnderlyingType()
|
||||||
(
|
|
|
||||||
if lower > typeLower or upper < typeUpper
|
t.isUnsigned() and bits = t.getSize() * 8
|
||||||
then reason = TValueFlowAnalysis()
|
|
||||||
else reason = TTypeBoundsAnalysis()
|
|
||||||
)
|
)
|
||||||
) and
|
) and
|
||||||
len = valueBasedBound.minimum(typeBasedBound)
|
len = (sizeBits / 10.0.log2()).ceil()
|
||||||
|
// convert the size from bits to decimal characters, and round up as you can't have
|
||||||
|
// fractional characters (10.0.log2() is the number of bits expressed per decimal character)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
this.getConversionChar(n).toLowerCase() = "x" and
|
this.getConversionChar(n).toLowerCase() = "x" and
|
||||||
@@ -1236,8 +1107,7 @@ class FormatLiteral extends Literal {
|
|||||||
(
|
(
|
||||||
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||||
)
|
)
|
||||||
) and
|
)
|
||||||
reason = TTypeBoundsAnalysis()
|
|
||||||
or
|
or
|
||||||
this.getConversionChar(n).toLowerCase() = "p" and
|
this.getConversionChar(n).toLowerCase() = "p" and
|
||||||
exists(PointerType ptrType, int baseLen |
|
exists(PointerType ptrType, int baseLen |
|
||||||
@@ -1246,8 +1116,7 @@ class FormatLiteral extends Literal {
|
|||||||
(
|
(
|
||||||
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
if this.hasAlternateFlag(n) then len = 2 + baseLen else len = baseLen // "0x"
|
||||||
)
|
)
|
||||||
) and
|
)
|
||||||
reason = TValueFlowAnalysis()
|
|
||||||
or
|
or
|
||||||
this.getConversionChar(n).toLowerCase() = "o" and
|
this.getConversionChar(n).toLowerCase() = "o" and
|
||||||
// e.g. 2^32 - 1 = "37777777777"
|
// e.g. 2^32 - 1 = "37777777777"
|
||||||
@@ -1266,16 +1135,14 @@ class FormatLiteral extends Literal {
|
|||||||
(
|
(
|
||||||
if this.hasAlternateFlag(n) then len = 1 + baseLen else len = baseLen // "0"
|
if this.hasAlternateFlag(n) then len = 1 + baseLen else len = baseLen // "0"
|
||||||
)
|
)
|
||||||
) and
|
)
|
||||||
reason = TTypeBoundsAnalysis()
|
|
||||||
or
|
or
|
||||||
this.getConversionChar(n).toLowerCase() = "s" and
|
this.getConversionChar(n).toLowerCase() = "s" and
|
||||||
len =
|
len =
|
||||||
min(int v |
|
min(int v |
|
||||||
v = this.getPrecision(n) or
|
v = this.getPrecision(n) or
|
||||||
v = this.getUse().getFormatArgument(n).(AnalysedString).getMaxLength() - 1 // (don't count null terminator)
|
v = this.getUse().getFormatArgument(n).(AnalysedString).getMaxLength() - 1 // (don't count null terminator)
|
||||||
) and
|
)
|
||||||
reason = TValueFlowAnalysis()
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1287,19 +1154,10 @@ class FormatLiteral extends Literal {
|
|||||||
* determining whether a buffer overflow is caused by long float to string
|
* determining whether a buffer overflow is caused by long float to string
|
||||||
* conversions.
|
* conversions.
|
||||||
*/
|
*/
|
||||||
int getMaxConvertedLengthLimited(int n) { result = max(getMaxConvertedLengthLimited(n, _)) }
|
int getMaxConvertedLengthLimited(int n) {
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the maximum length of the string that can be produced by the nth
|
|
||||||
* conversion specifier of this format string, specifying the reason for the
|
|
||||||
* estimation, except that float to string conversions are assumed to be 8
|
|
||||||
* characters. This is helpful for determining whether a buffer overflow is
|
|
||||||
* caused by long float to string conversions.
|
|
||||||
*/
|
|
||||||
int getMaxConvertedLengthLimited(int n, BufferWriteEstimationReason reason) {
|
|
||||||
if this.getConversionChar(n).toLowerCase() = "f"
|
if this.getConversionChar(n).toLowerCase() = "f"
|
||||||
then result = this.getMaxConvertedLength(n, reason).minimum(8)
|
then result = this.getMaxConvertedLength(n).minimum(8)
|
||||||
else result = this.getMaxConvertedLength(n, reason)
|
else result = this.getMaxConvertedLength(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1339,35 +1197,29 @@ class FormatLiteral extends Literal {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getMaxConvertedLengthAfter(int n, BufferWriteEstimationReason reason) {
|
private int getMaxConvertedLengthAfter(int n) {
|
||||||
if n = this.getNumConvSpec()
|
if n = this.getNumConvSpec()
|
||||||
then result = this.getConstantSuffix().length() + 1 and reason = TValueFlowAnalysis()
|
then result = this.getConstantSuffix().length() + 1
|
||||||
else
|
else
|
||||||
exists(BufferWriteEstimationReason headReason, BufferWriteEstimationReason tailReason |
|
|
||||||
result =
|
result =
|
||||||
this.getConstantPart(n).length() + this.getMaxConvertedLength(n, headReason) +
|
this.getConstantPart(n).length() + this.getMaxConvertedLength(n) +
|
||||||
this.getMaxConvertedLengthAfter(n + 1, tailReason) and
|
this.getMaxConvertedLengthAfter(n + 1)
|
||||||
reason = headReason.combineWith(tailReason)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getMaxConvertedLengthAfterLimited(int n, BufferWriteEstimationReason reason) {
|
private int getMaxConvertedLengthAfterLimited(int n) {
|
||||||
if n = this.getNumConvSpec()
|
if n = this.getNumConvSpec()
|
||||||
then result = this.getConstantSuffix().length() + 1 and reason = TValueFlowAnalysis()
|
then result = this.getConstantSuffix().length() + 1
|
||||||
else
|
else
|
||||||
exists(BufferWriteEstimationReason headReason, BufferWriteEstimationReason tailReason |
|
|
||||||
result =
|
result =
|
||||||
this.getConstantPart(n).length() + this.getMaxConvertedLengthLimited(n, headReason) +
|
this.getConstantPart(n).length() + this.getMaxConvertedLengthLimited(n) +
|
||||||
this.getMaxConvertedLengthAfterLimited(n + 1, tailReason) and
|
this.getMaxConvertedLengthAfterLimited(n + 1)
|
||||||
reason = headReason.combineWith(tailReason)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the maximum length of the string that can be produced by this format
|
* Gets the maximum length of the string that can be produced by this format
|
||||||
* string. Has no result if this cannot be determined.
|
* string. Has no result if this cannot be determined.
|
||||||
*/
|
*/
|
||||||
int getMaxConvertedLength() { result = this.getMaxConvertedLengthAfter(0, _) }
|
int getMaxConvertedLength() { result = this.getMaxConvertedLengthAfter(0) }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the maximum length of the string that can be produced by this format
|
* Gets the maximum length of the string that can be produced by this format
|
||||||
@@ -1375,24 +1227,5 @@ class FormatLiteral extends Literal {
|
|||||||
* characters. This is helpful for determining whether a buffer overflow
|
* characters. This is helpful for determining whether a buffer overflow
|
||||||
* is caused by long float to string conversions.
|
* is caused by long float to string conversions.
|
||||||
*/
|
*/
|
||||||
int getMaxConvertedLengthLimited() { result = this.getMaxConvertedLengthAfterLimited(0, _) }
|
int getMaxConvertedLengthLimited() { result = this.getMaxConvertedLengthAfterLimited(0) }
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the maximum length of the string that can be produced by this format
|
|
||||||
* string, specifying the reason for the estimate. Has no result if no estimate
|
|
||||||
* can be found.
|
|
||||||
*/
|
|
||||||
int getMaxConvertedLengthWithReason(BufferWriteEstimationReason reason) {
|
|
||||||
result = this.getMaxConvertedLengthAfter(0, reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the maximum length of the string that can be produced by this format
|
|
||||||
* string, specifying the reason for the estimate, except that float to string
|
|
||||||
* conversions are assumed to be 8 characters. This is helpful for determining
|
|
||||||
* whether a buffer overflow is caused by long float to string conversions.
|
|
||||||
*/
|
|
||||||
int getMaxConvertedLengthLimitedWithReason(BufferWriteEstimationReason reason) {
|
|
||||||
result = this.getMaxConvertedLengthAfterLimited(0, reason)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ predicate definitionUsePair(SemanticStackVariable var, Expr def, Expr use) {
|
|||||||
* Holds if the definition `def` of some stack variable can reach `node`, which
|
* Holds if the definition `def` of some stack variable can reach `node`, which
|
||||||
* is a definition or use, without crossing definitions of the same variable.
|
* is a definition or use, without crossing definitions of the same variable.
|
||||||
*/
|
*/
|
||||||
predicate definitionReaches(Expr def, Expr node) { def.(Def).reaches(true, _, node) }
|
predicate definitionReaches(Expr def, Expr node) { def.(Def).reaches(true, _, node.(DefOrUse)) }
|
||||||
|
|
||||||
private predicate hasAddressOfAccess(SemanticStackVariable var) {
|
private predicate hasAddressOfAccess(SemanticStackVariable var) {
|
||||||
var.getAnAccess().isAddressOfAccessNonConst()
|
var.getAnAccess().isAddressOfAccessNonConst()
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class SsaDefinition extends ControlFlowNodeBase {
|
|||||||
BasicBlock getBasicBlock() { result.contains(this.getDefinition()) }
|
BasicBlock getBasicBlock() { result.contains(this.getDefinition()) }
|
||||||
|
|
||||||
/** Holds if this definition is a phi node for variable `v`. */
|
/** Holds if this definition is a phi node for variable `v`. */
|
||||||
predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this)) }
|
predicate isPhiNode(StackVariable v) { exists(StandardSSA x | x.phi_node(v, this.(BasicBlock))) }
|
||||||
|
|
||||||
/** Gets the location of this definition. */
|
/** Gets the location of this definition. */
|
||||||
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
|
Location getLocation() { result = this.(ControlFlowNode).getLocation() }
|
||||||
|
|||||||
@@ -153,11 +153,9 @@ library class SSAHelper extends int {
|
|||||||
* Modern Compiler Implementation by Andrew Appel.
|
* Modern Compiler Implementation by Andrew Appel.
|
||||||
*/
|
*/
|
||||||
private predicate frontier_phi_node(StackVariable v, BasicBlock b) {
|
private predicate frontier_phi_node(StackVariable v, BasicBlock b) {
|
||||||
exists(BasicBlock x |
|
exists(BasicBlock x | dominanceFrontier(x, b) and ssa_defn_rec(v, x)) and
|
||||||
dominanceFrontier(x, b) and ssa_defn_rec(pragma[only_bind_into](v), pragma[only_bind_into](x))
|
|
||||||
) and
|
|
||||||
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
|
/* We can also eliminate those nodes where the variable is not live on any incoming edge */
|
||||||
live_at_start_of_bb(pragma[only_bind_into](v), b)
|
live_at_start_of_bb(v, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate ssa_defn_rec(StackVariable v, BasicBlock b) {
|
private predicate ssa_defn_rec(StackVariable v, BasicBlock b) {
|
||||||
@@ -294,7 +292,7 @@ library class SSAHelper extends int {
|
|||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
string toString(ControlFlowNode node, StackVariable v) {
|
string toString(ControlFlowNode node, StackVariable v) {
|
||||||
if phi_node(v, node)
|
if phi_node(v, node.(BasicBlock))
|
||||||
then result = "SSA phi(" + v.getName() + ")"
|
then result = "SSA phi(" + v.getName() + ")"
|
||||||
else (
|
else (
|
||||||
ssa_defn(v, node, _, _) and result = "SSA def(" + v.getName() + ")"
|
ssa_defn(v, node, _, _) and result = "SSA def(" + v.getName() + ")"
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ private class PostOrderInitializer extends Initializer {
|
|||||||
or
|
or
|
||||||
this.getDeclaration() = for.getRangeVariable()
|
this.getDeclaration() = for.getRangeVariable()
|
||||||
or
|
or
|
||||||
this.getDeclaration() = for.getBeginEndDeclaration().getADeclaration()
|
this.getDeclaration() = for.getBeginEndDeclaration().(DeclStmt).getADeclaration()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1143,7 +1143,7 @@ private class ExceptionSource extends Node {
|
|||||||
this.reachesParent(mid) and
|
this.reachesParent(mid) and
|
||||||
not mid = any(TryStmt try).getStmt() and
|
not mid = any(TryStmt try).getStmt() and
|
||||||
not mid = any(MicrosoftTryStmt try).getStmt() and
|
not mid = any(MicrosoftTryStmt try).getStmt() and
|
||||||
parent = mid.getParentNode()
|
parent = mid.(Node).getParentNode()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -484,7 +484,7 @@ library class ExprEvaluator extends int {
|
|||||||
this.interestingInternal(e, req, true) and
|
this.interestingInternal(e, req, true) and
|
||||||
(
|
(
|
||||||
result = req.(CompileTimeConstantInt).getIntValue() or
|
result = req.(CompileTimeConstantInt).getIntValue() or
|
||||||
result = this.getCompoundValue(e, req)
|
result = this.getCompoundValue(e, req.(CompileTimeVariableExpr))
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
req.getUnderlyingType().(IntegralType).isSigned() or
|
req.getUnderlyingType().(IntegralType).isSigned() or
|
||||||
@@ -611,7 +611,7 @@ library class ExprEvaluator extends int {
|
|||||||
or
|
or
|
||||||
exists(AssignExpr req | req = val | result = this.getValueInternal(e, req.getRValue()))
|
exists(AssignExpr req | req = val | result = this.getValueInternal(e, req.getRValue()))
|
||||||
or
|
or
|
||||||
result = this.getVariableValue(e, val)
|
result = this.getVariableValue(e, val.(VariableAccess))
|
||||||
or
|
or
|
||||||
exists(FunctionCall call | call = val and not callWithMultipleTargets(call) |
|
exists(FunctionCall call | call = val and not callWithMultipleTargets(call) |
|
||||||
result = this.getFunctionValue(call.getTarget())
|
result = this.getFunctionValue(call.getTarget())
|
||||||
@@ -626,9 +626,9 @@ library class ExprEvaluator extends int {
|
|||||||
// All assignments must have the same int value
|
// All assignments must have the same int value
|
||||||
result =
|
result =
|
||||||
unique(Expr value |
|
unique(Expr value |
|
||||||
value = v.getAnAssignedValue() and not this.ignoreVariableAssignment(e, v, value)
|
value = v.getAnAssignedValue() and not ignoreVariableAssignment(e, v, value)
|
||||||
|
|
|
|
||||||
this.getValueInternalNonSubExpr(value)
|
getValueInternalNonSubExpr(value)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -663,7 +663,7 @@ library class ExprEvaluator extends int {
|
|||||||
this.interestingInternal(_, req, false) and
|
this.interestingInternal(_, req, false) and
|
||||||
(
|
(
|
||||||
result = req.(CompileTimeConstantInt).getIntValue() or
|
result = req.(CompileTimeConstantInt).getIntValue() or
|
||||||
result = this.getCompoundValueNonSubExpr(req)
|
result = this.getCompoundValueNonSubExpr(req.(CompileTimeVariableExpr))
|
||||||
) and
|
) and
|
||||||
(
|
(
|
||||||
req.getUnderlyingType().(IntegralType).isSigned() or
|
req.getUnderlyingType().(IntegralType).isSigned() or
|
||||||
@@ -787,7 +787,7 @@ library class ExprEvaluator extends int {
|
|||||||
or
|
or
|
||||||
exists(AssignExpr req | req = val | result = this.getValueInternalNonSubExpr(req.getRValue()))
|
exists(AssignExpr req | req = val | result = this.getValueInternalNonSubExpr(req.getRValue()))
|
||||||
or
|
or
|
||||||
result = this.getVariableValueNonSubExpr(val)
|
result = this.getVariableValueNonSubExpr(val.(VariableAccess))
|
||||||
or
|
or
|
||||||
exists(FunctionCall call | call = val and not callWithMultipleTargets(call) |
|
exists(FunctionCall call | call = val and not callWithMultipleTargets(call) |
|
||||||
result = this.getFunctionValue(call.getTarget())
|
result = this.getFunctionValue(call.getTarget())
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
private import cpp
|
private import cpp
|
||||||
private import semmle.code.cpp.dataflow.internal.DataFlowPrivate
|
|
||||||
private import semmle.code.cpp.dataflow.internal.DataFlowUtil
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a function that might be called by `call`.
|
* Gets a function that might be called by `call`.
|
||||||
@@ -65,17 +63,3 @@ predicate mayBenefitFromCallContext(Call call, Function f) { none() }
|
|||||||
* restricted to those `call`s for which a context might make a difference.
|
* restricted to those `call`s for which a context might make a difference.
|
||||||
*/
|
*/
|
||||||
Function viableImplInCallContext(Call call, Call ctx) { none() }
|
Function viableImplInCallContext(Call call, Call ctx) { none() }
|
||||||
|
|
||||||
/** A parameter position represented by an integer. */
|
|
||||||
class ParameterPosition extends int {
|
|
||||||
ParameterPosition() { any(ParameterNode p).isParameterOf(_, this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** An argument position represented by an integer. */
|
|
||||||
class ArgumentPosition extends int {
|
|
||||||
ArgumentPosition() { any(ArgumentNode a).argumentOf(_, this) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if arguments at position `apos` match parameters at position `ppos`. */
|
|
||||||
pragma[inline]
|
|
||||||
predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos }
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
import DataFlowImplCommonPublic
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A configuration of interprocedural data flow analysis. This defines
|
* A configuration of interprocedural data flow analysis. This defines
|
||||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
int fieldFlowBranchLimit() { result = 2 }
|
int fieldFlowBranchLimit() { result = 2 }
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a data flow configuration feature to add restrictions to the set of
|
|
||||||
* valid flow paths.
|
|
||||||
*
|
|
||||||
* - `FeatureHasSourceCallContext`:
|
|
||||||
* Assume that sources have some existing call context to disallow
|
|
||||||
* conflicting return-flow directly following the source.
|
|
||||||
* - `FeatureHasSinkCallContext`:
|
|
||||||
* Assume that sinks have some existing call context to disallow
|
|
||||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
|
||||||
* - `FeatureEqualSourceSinkCallContext`:
|
|
||||||
* Implies both of the above and additionally ensures that the entire flow
|
|
||||||
* path preserves the call context.
|
|
||||||
*/
|
|
||||||
FlowFeature getAFeature() { none() }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||||
*/
|
*/
|
||||||
@@ -256,11 +239,11 @@ private class ArgNodeEx extends NodeEx {
|
|||||||
private class ParamNodeEx extends NodeEx {
|
private class ParamNodeEx extends NodeEx {
|
||||||
ParamNodeEx() { this.asNode() instanceof ParamNode }
|
ParamNodeEx() { this.asNode() instanceof ParamNode }
|
||||||
|
|
||||||
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
predicate isParameterOf(DataFlowCallable c, int i) {
|
||||||
this.asNode().(ParamNode).isParameterOf(c, pos)
|
this.asNode().(ParamNode).isParameterOf(c, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
int getPosition() { this.isParameterOf(_, result) }
|
||||||
|
|
||||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||||
}
|
}
|
||||||
@@ -289,7 +272,6 @@ private predicate outBarrier(NodeEx node, Configuration config) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate fullBarrier(NodeEx node, Configuration config) {
|
private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||||
exists(Node n | node.asNode() = n |
|
exists(Node n | node.asNode() = n |
|
||||||
config.isBarrier(n)
|
config.isBarrier(n)
|
||||||
@@ -308,23 +290,11 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate sourceNode(NodeEx node, Configuration config) {
|
private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
|
||||||
config.isSource(node.asNode()) and
|
|
||||||
not fullBarrier(node, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
|
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
|
||||||
|
|
||||||
/** Provides the relevant barriers for a step from `node1` to `node2`. */
|
|
||||||
pragma[inline]
|
|
||||||
private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) {
|
|
||||||
not outBarrier(node1, config) and
|
|
||||||
not inBarrier(node2, config) and
|
|
||||||
not fullBarrier(node1, config) and
|
|
||||||
not fullBarrier(node2, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||||
*/
|
*/
|
||||||
@@ -333,14 +303,16 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
|
|||||||
node1.asNode() = n1 and
|
node1.asNode() = n1 and
|
||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
simpleLocalFlowStepExt(n1, n2) and
|
simpleLocalFlowStepExt(n1, n2) and
|
||||||
stepFilter(node1, node2, config)
|
not outBarrier(node1, config) and
|
||||||
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
config.allowImplicitRead(n, _) and
|
config.allowImplicitRead(n, _) and
|
||||||
node1.asNode() = n and
|
node1.asNode() = n and
|
||||||
node2.isImplicitReadNode(n, false) and
|
node2.isImplicitReadNode(n, false)
|
||||||
not fullBarrier(node1, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,14 +325,16 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
|
|||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
config.isAdditionalFlowStep(n1, n2) and
|
config.isAdditionalFlowStep(n1, n2) and
|
||||||
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
|
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
|
||||||
stepFilter(node1, node2, config)
|
not outBarrier(node1, config) and
|
||||||
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
config.allowImplicitRead(n, _) and
|
config.allowImplicitRead(n, _) and
|
||||||
node1.isImplicitReadNode(n, true) and
|
node1.isImplicitReadNode(n, true) and
|
||||||
node2.asNode() = n and
|
node2.asNode() = n
|
||||||
not fullBarrier(node2, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,8 +346,10 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
|||||||
node1.asNode() = n1 and
|
node1.asNode() = n1 and
|
||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
jumpStepCached(n1, n2) and
|
jumpStepCached(n1, n2) and
|
||||||
stepFilter(node1, node2, config) and
|
not outBarrier(node1, config) and
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,14 +362,15 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
|||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
config.isAdditionalFlowStep(n1, n2) and
|
config.isAdditionalFlowStep(n1, n2) and
|
||||||
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
|
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
|
||||||
stepFilter(node1, node2, config) and
|
not outBarrier(node1, config) and
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||||
read(node1.asNode(), c, node2.asNode()) and
|
read(node1.asNode(), c, node2.asNode())
|
||||||
stepFilter(node1, node2, config)
|
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
node2.isImplicitReadNode(n, true) and
|
node2.isImplicitReadNode(n, true) and
|
||||||
@@ -406,8 +383,7 @@ private predicate store(
|
|||||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||||
) {
|
) {
|
||||||
store(node1.asNode(), tc, node2.asNode(), contentType) and
|
store(node1.asNode(), tc, node2.asNode(), contentType) and
|
||||||
read(_, tc.getContent(), _, config) and
|
read(_, tc.getContent(), _, config)
|
||||||
stepFilter(node1, node2, config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
@@ -425,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
|||||||
*/
|
*/
|
||||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||||
|
|
||||||
private predicate hasSourceCallCtx(Configuration config) {
|
|
||||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
|
||||||
feature instanceof FeatureHasSourceCallContext or
|
|
||||||
feature instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate hasSinkCallCtx(Configuration config) {
|
|
||||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
|
||||||
feature instanceof FeatureHasSinkCallContext or
|
|
||||||
feature instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private module Stage1 {
|
private module Stage1 {
|
||||||
class ApApprox = Unit;
|
class ApApprox = Unit;
|
||||||
|
|
||||||
@@ -456,8 +418,10 @@ private module Stage1 {
|
|||||||
* argument in a call.
|
* argument in a call.
|
||||||
*/
|
*/
|
||||||
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||||
|
not fullBarrier(node, config) and
|
||||||
|
(
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
cc = false
|
||||||
or
|
or
|
||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
fwdFlow(mid, cc, config) and
|
fwdFlow(mid, cc, config) and
|
||||||
@@ -485,21 +449,22 @@ private module Stage1 {
|
|||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
useFieldFlow(config) and
|
useFieldFlow(config) and
|
||||||
fwdFlow(mid, cc, config) and
|
fwdFlow(mid, cc, config) and
|
||||||
store(mid, _, node, _, config)
|
store(mid, _, node, _, config) and
|
||||||
|
not outBarrier(mid, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// read
|
// read
|
||||||
exists(Content c |
|
exists(Content c |
|
||||||
fwdFlowRead(c, node, cc, config) and
|
fwdFlowRead(c, node, cc, config) and
|
||||||
fwdFlowConsCand(c, config)
|
fwdFlowConsCand(c, config) and
|
||||||
|
not inBarrier(node, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow into a callable
|
// flow into a callable
|
||||||
exists(NodeEx arg |
|
exists(NodeEx arg |
|
||||||
fwdFlow(arg, _, config) and
|
fwdFlow(arg, _, config) and
|
||||||
viableParamArgEx(_, node, arg) and
|
viableParamArgEx(_, node, arg) and
|
||||||
cc = true and
|
cc = true
|
||||||
not fullBarrier(node, config)
|
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
@@ -510,6 +475,7 @@ private module Stage1 {
|
|||||||
fwdFlowOutFromArg(call, node, config) and
|
fwdFlowOutFromArg(call, node, config) and
|
||||||
fwdFlowIsEntered(call, cc, config)
|
fwdFlowIsEntered(call, cc, config)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||||
@@ -548,8 +514,7 @@ private module Stage1 {
|
|||||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||||
exists(ReturnPosition pos |
|
exists(ReturnPosition pos |
|
||||||
fwdFlowReturnPosition(pos, cc, config) and
|
fwdFlowReturnPosition(pos, cc, config) and
|
||||||
viableReturnPosOutEx(call, pos, out) and
|
viableReturnPosOutEx(call, pos, out)
|
||||||
not fullBarrier(out, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +551,7 @@ private module Stage1 {
|
|||||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||||
fwdFlow(node, config) and
|
fwdFlow(node, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
toReturn = false
|
||||||
or
|
or
|
||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
localFlowStep(node, mid, config) and
|
localFlowStep(node, mid, config) and
|
||||||
@@ -775,7 +740,6 @@ private module Stage1 {
|
|||||||
* Holds if flow may enter through `p` and reach a return node making `p` a
|
* Holds if flow may enter through `p` and reach a return node making `p` a
|
||||||
* candidate for the origin of a summary.
|
* candidate for the origin of a summary.
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(ReturnKindExt kind |
|
exists(ReturnKindExt kind |
|
||||||
throughFlowNodeCand(p, config) and
|
throughFlowNodeCand(p, config) and
|
||||||
@@ -973,8 +937,6 @@ private module Stage2 {
|
|||||||
|
|
||||||
Cc ccNone() { result instanceof CallContextAny }
|
Cc ccNone() { result instanceof CallContextAny }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
|
||||||
|
|
||||||
private class LocalCc = Unit;
|
private class LocalCc = Unit;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -1012,9 +974,6 @@ private module Stage2 {
|
|||||||
|
|
||||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||||
|
|
||||||
bindingset[node, ap]
|
|
||||||
private predicate filter(NodeEx node, Ap ap) { any() }
|
|
||||||
|
|
||||||
bindingset[ap, contentType]
|
bindingset[ap, contentType]
|
||||||
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||||
|
|
||||||
@@ -1023,23 +982,14 @@ private module Stage2 {
|
|||||||
PrevStage::revFlow(node, _, _, apa, config)
|
PrevStage::revFlow(node, _, _, apa, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingset[result, apa]
|
|
||||||
private ApApprox unbindApa(ApApprox apa) {
|
|
||||||
exists(ApApprox apa0 |
|
|
||||||
apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1052,16 +1002,9 @@ private module Stage2 {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
fwdFlow0(node, cc, argAp, ap, config) and
|
|
||||||
flowCand(node, unbindApa(getApprox(ap)), config) and
|
|
||||||
filter(node, ap)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -1129,7 +1072,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(DataFlowType contentType |
|
exists(DataFlowType contentType |
|
||||||
fwdFlow(node1, cc, argAp, ap1, config) and
|
fwdFlow(node1, cc, argAp, ap1, config) and
|
||||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
|
||||||
typecheckStore(ap1, contentType)
|
typecheckStore(ap1, contentType)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1164,8 +1107,9 @@ private module Stage2 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1180,8 +1124,9 @@ private module Stage2 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,8 +1136,10 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,7 +1153,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p |
|
exists(ParamNodeEx p |
|
||||||
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
|
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
|
||||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1248,11 +1195,6 @@ private module Stage2 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -1273,7 +1215,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -1329,7 +1271,7 @@ private module Stage2 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -1364,8 +1306,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,8 +1318,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1386,8 +1330,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1447,7 +1392,7 @@ private module Stage2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -1671,8 +1616,6 @@ private module Stage3 {
|
|||||||
|
|
||||||
Cc ccNone() { result = false }
|
Cc ccNone() { result = false }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result = true }
|
|
||||||
|
|
||||||
private class LocalCc = Unit;
|
private class LocalCc = Unit;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -1727,14 +1670,12 @@ private module Stage3 {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1756,7 +1697,7 @@ private module Stage3 {
|
|||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -1859,8 +1800,9 @@ private module Stage3 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1875,8 +1817,9 @@ private module Stage3 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1886,8 +1829,10 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1943,11 +1888,6 @@ private module Stage3 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -1968,7 +1908,7 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -2024,7 +1964,7 @@ private module Stage3 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -2059,8 +1999,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2070,8 +2011,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2081,8 +2023,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2142,7 +2085,7 @@ private module Stage3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -2423,8 +2366,6 @@ private module Stage4 {
|
|||||||
|
|
||||||
Cc ccNone() { result instanceof CallContextAny }
|
Cc ccNone() { result instanceof CallContextAny }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
|
||||||
|
|
||||||
private class LocalCc = LocalCallContext;
|
private class LocalCc = LocalCallContext;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -2493,14 +2434,12 @@ private module Stage4 {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2522,7 +2461,7 @@ private module Stage4 {
|
|||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -2625,8 +2564,9 @@ private module Stage4 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2641,8 +2581,9 @@ private module Stage4 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2652,8 +2593,10 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2709,11 +2652,6 @@ private module Stage4 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -2734,7 +2672,7 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -2790,7 +2728,7 @@ private module Stage4 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -2825,8 +2763,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2836,8 +2775,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2847,8 +2787,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2908,7 +2849,7 @@ private module Stage4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -2992,7 +2933,7 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
|||||||
|
|
||||||
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
|
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
|
||||||
|
|
||||||
ParameterPosition getParameterPos() { p.isParameterOf(_, result) }
|
int getParameterPos() { p.isParameterOf(_, result) }
|
||||||
|
|
||||||
ParamNodeEx getParamNode() { result = p }
|
ParamNodeEx getParamNode() { result = p }
|
||||||
|
|
||||||
@@ -3123,11 +3064,7 @@ private newtype TPathNode =
|
|||||||
// A PathNode is introduced by a source ...
|
// A PathNode is introduced by a source ...
|
||||||
Stage4::revFlow(node, config) and
|
Stage4::revFlow(node, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(
|
cc instanceof CallContextAny and
|
||||||
if hasSourceCallCtx(config)
|
|
||||||
then cc instanceof CallContextSomeCall
|
|
||||||
else cc instanceof CallContextAny
|
|
||||||
) and
|
|
||||||
sc instanceof SummaryCtxNone and
|
sc instanceof SummaryCtxNone and
|
||||||
ap = TAccessPathNil(node.getDataFlowType())
|
ap = TAccessPathNil(node.getDataFlowType())
|
||||||
or
|
or
|
||||||
@@ -3139,10 +3076,17 @@ private newtype TPathNode =
|
|||||||
)
|
)
|
||||||
} or
|
} or
|
||||||
TPathNodeSink(NodeEx node, Configuration config) {
|
TPathNodeSink(NodeEx node, Configuration config) {
|
||||||
exists(PathNodeMid sink |
|
sinkNode(node, pragma[only_bind_into](config)) and
|
||||||
sink.isAtSink() and
|
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||||
node = sink.getNodeEx() and
|
(
|
||||||
config = sink.getConfiguration()
|
// A sink that is also a source ...
|
||||||
|
sourceNode(node, config)
|
||||||
|
or
|
||||||
|
// ... or a sink that can be reached from a source
|
||||||
|
exists(PathNodeMid mid |
|
||||||
|
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||||
|
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3459,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
|||||||
// an intermediate step to another intermediate node
|
// an intermediate step to another intermediate node
|
||||||
result = this.getSuccMid()
|
result = this.getSuccMid()
|
||||||
or
|
or
|
||||||
// a final step to a sink
|
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||||
result = this.getSuccMid().projectToSink()
|
exists(PathNodeMid mid, PathNodeSink sink |
|
||||||
|
mid = this.getSuccMid() and
|
||||||
|
mid.getNodeEx() = sink.getNodeEx() and
|
||||||
|
mid.getAp() instanceof AccessPathNil and
|
||||||
|
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||||
|
result = sink
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSource() {
|
override predicate isSource() {
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(
|
cc instanceof CallContextAny and
|
||||||
if hasSourceCallCtx(config)
|
|
||||||
then cc instanceof CallContextSomeCall
|
|
||||||
else cc instanceof CallContextAny
|
|
||||||
) and
|
|
||||||
sc instanceof SummaryCtxNone and
|
sc instanceof SummaryCtxNone and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate isAtSink() {
|
|
||||||
sinkNode(node, config) and
|
|
||||||
ap instanceof AccessPathNil and
|
|
||||||
if hasSinkCallCtx(config)
|
|
||||||
then
|
|
||||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
|
||||||
// is exactly what we need to check. This also implies
|
|
||||||
// `sc instanceof SummaryCtxNone`.
|
|
||||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
|
||||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
|
||||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
|
||||||
// in this case there's never any need to enter a call except to identify
|
|
||||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
|
||||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
|
||||||
// in the call context of the source.
|
|
||||||
sc instanceof SummaryCtxNone or
|
|
||||||
cc instanceof CallContextNoCall
|
|
||||||
else any()
|
|
||||||
}
|
|
||||||
|
|
||||||
PathNodeSink projectToSink() {
|
|
||||||
this.isAtSink() and
|
|
||||||
result.getNodeEx() = node and
|
|
||||||
result.getConfiguration() = unbindConf(config)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3639,40 +3559,39 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
|||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate pathIntoArg(
|
private predicate pathIntoArg(
|
||||||
PathNodeMid mid, ParameterPosition ppos, CallContext cc, DataFlowCall call, AccessPath ap,
|
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||||
AccessPathApprox apa, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ArgNode arg, ArgumentPosition apos |
|
exists(ArgNode arg |
|
||||||
arg = mid.getNodeEx().asNode() and
|
arg = mid.getNodeEx().asNode() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
arg.argumentOf(call, apos) and
|
arg.argumentOf(call, i) and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
apa = ap.getApprox() and
|
apa = ap.getApprox() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[noinline]
|
||||||
private predicate parameterCand(
|
private predicate parameterCand(
|
||||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p |
|
exists(ParamNodeEx p |
|
||||||
Stage4::revFlow(p, _, _, apa, config) and
|
Stage4::revFlow(p, _, _, apa, config) and
|
||||||
p.isParameterOf(callable, pos)
|
p.isParameterOf(callable, i)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate pathIntoCallable0(
|
private predicate pathIntoCallable0(
|
||||||
PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, CallContext outercc,
|
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||||
DataFlowCall call, AccessPath ap, Configuration config
|
AccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(AccessPathApprox apa |
|
exists(AccessPathApprox apa |
|
||||||
pathIntoArg(mid, pragma[only_bind_into](pos), outercc, call, ap, pragma[only_bind_into](apa),
|
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config)) and
|
||||||
callable = resolveCall(call, outercc) and
|
callable = resolveCall(call, outercc) and
|
||||||
parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa),
|
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||||
pragma[only_bind_into](config))
|
pragma[only_bind_into](config))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3687,18 +3606,14 @@ private predicate pathIntoCallable(
|
|||||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||||
DataFlowCall call, Configuration config
|
DataFlowCall call, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParameterPosition pos, DataFlowCallable callable, AccessPath ap |
|
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||||
pathIntoCallable0(mid, callable, pos, outercc, call, ap, config) and
|
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||||
p.isParameterOf(callable, pos) and
|
p.isParameterOf(callable, i) and
|
||||||
(
|
(
|
||||||
sc = TSummaryCtxSome(p, ap)
|
sc = TSummaryCtxSome(p, ap)
|
||||||
or
|
or
|
||||||
not exists(TSummaryCtxSome(p, ap)) and
|
not exists(TSummaryCtxSome(p, ap)) and
|
||||||
sc = TSummaryCtxNone() and
|
sc = TSummaryCtxNone()
|
||||||
// When the call contexts of source and sink needs to match then there's
|
|
||||||
// never any reason to enter a callable except to find a summary. See also
|
|
||||||
// the comment in `PathNodeMid::isAtSink`.
|
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
)
|
||||||
|
|
|
|
||||||
if recordDataFlowCallSite(call, callable)
|
if recordDataFlowCallSite(call, callable)
|
||||||
@@ -3713,7 +3628,7 @@ private predicate paramFlowsThrough(
|
|||||||
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
|
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
exists(PathNodeMid mid, RetNodeEx ret, int pos |
|
||||||
mid.getNodeEx() = ret and
|
mid.getNodeEx() = ret and
|
||||||
kind = ret.getKind() and
|
kind = ret.getKind() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
@@ -3761,14 +3676,13 @@ private module Subpaths {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate subpaths01(
|
private predicate subpaths01(
|
||||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||||
NodeEx out, AccessPath apout
|
NodeEx out, AccessPath apout
|
||||||
) {
|
) {
|
||||||
exists(Configuration config |
|
exists(Configuration config |
|
||||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) and
|
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||||
not arg.isHidden()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3802,17 +3716,8 @@ private module Subpaths {
|
|||||||
innercc = ret.getCallContext() and
|
innercc = ret.getCallContext() and
|
||||||
sc = ret.getSummaryCtx() and
|
sc = ret.getSummaryCtx() and
|
||||||
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
||||||
apout = ret.getAp()
|
apout = ret.getAp() and
|
||||||
)
|
not ret.isHidden()
|
||||||
}
|
|
||||||
|
|
||||||
private PathNodeImpl localStepToHidden(PathNodeImpl n) {
|
|
||||||
n.getASuccessorImpl() = result and
|
|
||||||
result.isHidden() and
|
|
||||||
exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() |
|
|
||||||
localFlowBigStep(n1, n2, _, _, _, _) or
|
|
||||||
store(n1, _, n2, _, _) or
|
|
||||||
read(n1, _, n2, _)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3821,12 +3726,11 @@ private module Subpaths {
|
|||||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||||
*/
|
*/
|
||||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNodeMid out) {
|
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||||
subpaths03(arg, p, localStepToHidden*(ret), o, apout) and
|
subpaths03(arg, p, ret, o, apout) and
|
||||||
not ret.isHidden() and
|
|
||||||
par.getNodeEx() = p and
|
par.getNodeEx() = p and
|
||||||
out.getNodeEx() = o and
|
out.getNodeEx() = o and
|
||||||
out.getAp() = apout
|
out.getAp() = apout
|
||||||
@@ -4442,25 +4346,24 @@ private module FlowExploration {
|
|||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate partialPathIntoArg(
|
private predicate partialPathIntoArg(
|
||||||
PartialPathNodeFwd mid, ParameterPosition ppos, CallContext cc, DataFlowCall call,
|
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||||
PartialAccessPath ap, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ArgNode arg, ArgumentPosition apos |
|
exists(ArgNode arg |
|
||||||
arg = mid.getNodeEx().asNode() and
|
arg = mid.getNodeEx().asNode() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
arg.argumentOf(call, apos) and
|
arg.argumentOf(call, i) and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate partialPathIntoCallable0(
|
private predicate partialPathIntoCallable0(
|
||||||
PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, CallContext outercc,
|
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
partialPathIntoArg(mid, pos, outercc, call, ap, config) and
|
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||||
callable = resolveCall(call, outercc)
|
callable = resolveCall(call, outercc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4469,9 +4372,9 @@ private module FlowExploration {
|
|||||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParameterPosition pos, DataFlowCallable callable |
|
exists(int i, DataFlowCallable callable |
|
||||||
partialPathIntoCallable0(mid, callable, pos, outercc, call, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||||
p.isParameterOf(callable, pos) and
|
p.isParameterOf(callable, i) and
|
||||||
sc1 = TSummaryCtx1Param(p) and
|
sc1 = TSummaryCtx1Param(p) and
|
||||||
sc2 = TSummaryCtx2Some(ap)
|
sc2 = TSummaryCtx2Some(ap)
|
||||||
|
|
|
|
||||||
@@ -4635,23 +4538,22 @@ private module FlowExploration {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate revPartialPathFlowsThrough(
|
private predicate revPartialPathFlowsThrough(
|
||||||
ArgumentPosition apos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||||
RevPartialAccessPath ap, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos |
|
exists(PartialPathNodeRev mid, ParamNodeEx p |
|
||||||
mid.getNodeEx() = p and
|
mid.getNodeEx() = p and
|
||||||
p.getPosition() = ppos and
|
p.getPosition() = pos and
|
||||||
sc1 = mid.getSummaryCtx1() and
|
sc1 = mid.getSummaryCtx1() and
|
||||||
sc2 = mid.getSummaryCtx2() and
|
sc2 = mid.getSummaryCtx2() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate revPartialPathThroughCallable0(
|
private predicate revPartialPathThroughCallable0(
|
||||||
DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, RevPartialAccessPath ap,
|
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||||
@@ -4664,7 +4566,7 @@ private module FlowExploration {
|
|||||||
private predicate revPartialPathThroughCallable(
|
private predicate revPartialPathThroughCallable(
|
||||||
PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
|
PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(DataFlowCall call, ArgumentPosition pos |
|
exists(DataFlowCall call, int pos |
|
||||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||||
node.asNode().(ArgNode).argumentOf(call, pos)
|
node.asNode().(ArgNode).argumentOf(call, pos)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
import DataFlowImplCommonPublic
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A configuration of interprocedural data flow analysis. This defines
|
* A configuration of interprocedural data flow analysis. This defines
|
||||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
int fieldFlowBranchLimit() { result = 2 }
|
int fieldFlowBranchLimit() { result = 2 }
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a data flow configuration feature to add restrictions to the set of
|
|
||||||
* valid flow paths.
|
|
||||||
*
|
|
||||||
* - `FeatureHasSourceCallContext`:
|
|
||||||
* Assume that sources have some existing call context to disallow
|
|
||||||
* conflicting return-flow directly following the source.
|
|
||||||
* - `FeatureHasSinkCallContext`:
|
|
||||||
* Assume that sinks have some existing call context to disallow
|
|
||||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
|
||||||
* - `FeatureEqualSourceSinkCallContext`:
|
|
||||||
* Implies both of the above and additionally ensures that the entire flow
|
|
||||||
* path preserves the call context.
|
|
||||||
*/
|
|
||||||
FlowFeature getAFeature() { none() }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||||
*/
|
*/
|
||||||
@@ -256,11 +239,11 @@ private class ArgNodeEx extends NodeEx {
|
|||||||
private class ParamNodeEx extends NodeEx {
|
private class ParamNodeEx extends NodeEx {
|
||||||
ParamNodeEx() { this.asNode() instanceof ParamNode }
|
ParamNodeEx() { this.asNode() instanceof ParamNode }
|
||||||
|
|
||||||
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
predicate isParameterOf(DataFlowCallable c, int i) {
|
||||||
this.asNode().(ParamNode).isParameterOf(c, pos)
|
this.asNode().(ParamNode).isParameterOf(c, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
int getPosition() { this.isParameterOf(_, result) }
|
||||||
|
|
||||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||||
}
|
}
|
||||||
@@ -289,7 +272,6 @@ private predicate outBarrier(NodeEx node, Configuration config) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate fullBarrier(NodeEx node, Configuration config) {
|
private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||||
exists(Node n | node.asNode() = n |
|
exists(Node n | node.asNode() = n |
|
||||||
config.isBarrier(n)
|
config.isBarrier(n)
|
||||||
@@ -308,23 +290,11 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate sourceNode(NodeEx node, Configuration config) {
|
private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
|
||||||
config.isSource(node.asNode()) and
|
|
||||||
not fullBarrier(node, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
|
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
|
||||||
|
|
||||||
/** Provides the relevant barriers for a step from `node1` to `node2`. */
|
|
||||||
pragma[inline]
|
|
||||||
private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) {
|
|
||||||
not outBarrier(node1, config) and
|
|
||||||
not inBarrier(node2, config) and
|
|
||||||
not fullBarrier(node1, config) and
|
|
||||||
not fullBarrier(node2, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||||
*/
|
*/
|
||||||
@@ -333,14 +303,16 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
|
|||||||
node1.asNode() = n1 and
|
node1.asNode() = n1 and
|
||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
simpleLocalFlowStepExt(n1, n2) and
|
simpleLocalFlowStepExt(n1, n2) and
|
||||||
stepFilter(node1, node2, config)
|
not outBarrier(node1, config) and
|
||||||
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
config.allowImplicitRead(n, _) and
|
config.allowImplicitRead(n, _) and
|
||||||
node1.asNode() = n and
|
node1.asNode() = n and
|
||||||
node2.isImplicitReadNode(n, false) and
|
node2.isImplicitReadNode(n, false)
|
||||||
not fullBarrier(node1, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,14 +325,16 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
|
|||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
config.isAdditionalFlowStep(n1, n2) and
|
config.isAdditionalFlowStep(n1, n2) and
|
||||||
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
|
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
|
||||||
stepFilter(node1, node2, config)
|
not outBarrier(node1, config) and
|
||||||
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
config.allowImplicitRead(n, _) and
|
config.allowImplicitRead(n, _) and
|
||||||
node1.isImplicitReadNode(n, true) and
|
node1.isImplicitReadNode(n, true) and
|
||||||
node2.asNode() = n and
|
node2.asNode() = n
|
||||||
not fullBarrier(node2, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,8 +346,10 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
|||||||
node1.asNode() = n1 and
|
node1.asNode() = n1 and
|
||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
jumpStepCached(n1, n2) and
|
jumpStepCached(n1, n2) and
|
||||||
stepFilter(node1, node2, config) and
|
not outBarrier(node1, config) and
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,14 +362,15 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
|||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
config.isAdditionalFlowStep(n1, n2) and
|
config.isAdditionalFlowStep(n1, n2) and
|
||||||
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
|
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
|
||||||
stepFilter(node1, node2, config) and
|
not outBarrier(node1, config) and
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||||
read(node1.asNode(), c, node2.asNode()) and
|
read(node1.asNode(), c, node2.asNode())
|
||||||
stepFilter(node1, node2, config)
|
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
node2.isImplicitReadNode(n, true) and
|
node2.isImplicitReadNode(n, true) and
|
||||||
@@ -406,8 +383,7 @@ private predicate store(
|
|||||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||||
) {
|
) {
|
||||||
store(node1.asNode(), tc, node2.asNode(), contentType) and
|
store(node1.asNode(), tc, node2.asNode(), contentType) and
|
||||||
read(_, tc.getContent(), _, config) and
|
read(_, tc.getContent(), _, config)
|
||||||
stepFilter(node1, node2, config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
@@ -425,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
|||||||
*/
|
*/
|
||||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||||
|
|
||||||
private predicate hasSourceCallCtx(Configuration config) {
|
|
||||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
|
||||||
feature instanceof FeatureHasSourceCallContext or
|
|
||||||
feature instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate hasSinkCallCtx(Configuration config) {
|
|
||||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
|
||||||
feature instanceof FeatureHasSinkCallContext or
|
|
||||||
feature instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private module Stage1 {
|
private module Stage1 {
|
||||||
class ApApprox = Unit;
|
class ApApprox = Unit;
|
||||||
|
|
||||||
@@ -456,8 +418,10 @@ private module Stage1 {
|
|||||||
* argument in a call.
|
* argument in a call.
|
||||||
*/
|
*/
|
||||||
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||||
|
not fullBarrier(node, config) and
|
||||||
|
(
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
cc = false
|
||||||
or
|
or
|
||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
fwdFlow(mid, cc, config) and
|
fwdFlow(mid, cc, config) and
|
||||||
@@ -485,21 +449,22 @@ private module Stage1 {
|
|||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
useFieldFlow(config) and
|
useFieldFlow(config) and
|
||||||
fwdFlow(mid, cc, config) and
|
fwdFlow(mid, cc, config) and
|
||||||
store(mid, _, node, _, config)
|
store(mid, _, node, _, config) and
|
||||||
|
not outBarrier(mid, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// read
|
// read
|
||||||
exists(Content c |
|
exists(Content c |
|
||||||
fwdFlowRead(c, node, cc, config) and
|
fwdFlowRead(c, node, cc, config) and
|
||||||
fwdFlowConsCand(c, config)
|
fwdFlowConsCand(c, config) and
|
||||||
|
not inBarrier(node, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow into a callable
|
// flow into a callable
|
||||||
exists(NodeEx arg |
|
exists(NodeEx arg |
|
||||||
fwdFlow(arg, _, config) and
|
fwdFlow(arg, _, config) and
|
||||||
viableParamArgEx(_, node, arg) and
|
viableParamArgEx(_, node, arg) and
|
||||||
cc = true and
|
cc = true
|
||||||
not fullBarrier(node, config)
|
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
@@ -510,6 +475,7 @@ private module Stage1 {
|
|||||||
fwdFlowOutFromArg(call, node, config) and
|
fwdFlowOutFromArg(call, node, config) and
|
||||||
fwdFlowIsEntered(call, cc, config)
|
fwdFlowIsEntered(call, cc, config)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||||
@@ -548,8 +514,7 @@ private module Stage1 {
|
|||||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||||
exists(ReturnPosition pos |
|
exists(ReturnPosition pos |
|
||||||
fwdFlowReturnPosition(pos, cc, config) and
|
fwdFlowReturnPosition(pos, cc, config) and
|
||||||
viableReturnPosOutEx(call, pos, out) and
|
viableReturnPosOutEx(call, pos, out)
|
||||||
not fullBarrier(out, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +551,7 @@ private module Stage1 {
|
|||||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||||
fwdFlow(node, config) and
|
fwdFlow(node, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
toReturn = false
|
||||||
or
|
or
|
||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
localFlowStep(node, mid, config) and
|
localFlowStep(node, mid, config) and
|
||||||
@@ -775,7 +740,6 @@ private module Stage1 {
|
|||||||
* Holds if flow may enter through `p` and reach a return node making `p` a
|
* Holds if flow may enter through `p` and reach a return node making `p` a
|
||||||
* candidate for the origin of a summary.
|
* candidate for the origin of a summary.
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(ReturnKindExt kind |
|
exists(ReturnKindExt kind |
|
||||||
throughFlowNodeCand(p, config) and
|
throughFlowNodeCand(p, config) and
|
||||||
@@ -973,8 +937,6 @@ private module Stage2 {
|
|||||||
|
|
||||||
Cc ccNone() { result instanceof CallContextAny }
|
Cc ccNone() { result instanceof CallContextAny }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
|
||||||
|
|
||||||
private class LocalCc = Unit;
|
private class LocalCc = Unit;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -1012,9 +974,6 @@ private module Stage2 {
|
|||||||
|
|
||||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||||
|
|
||||||
bindingset[node, ap]
|
|
||||||
private predicate filter(NodeEx node, Ap ap) { any() }
|
|
||||||
|
|
||||||
bindingset[ap, contentType]
|
bindingset[ap, contentType]
|
||||||
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||||
|
|
||||||
@@ -1023,23 +982,14 @@ private module Stage2 {
|
|||||||
PrevStage::revFlow(node, _, _, apa, config)
|
PrevStage::revFlow(node, _, _, apa, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingset[result, apa]
|
|
||||||
private ApApprox unbindApa(ApApprox apa) {
|
|
||||||
exists(ApApprox apa0 |
|
|
||||||
apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1052,16 +1002,9 @@ private module Stage2 {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
fwdFlow0(node, cc, argAp, ap, config) and
|
|
||||||
flowCand(node, unbindApa(getApprox(ap)), config) and
|
|
||||||
filter(node, ap)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -1129,7 +1072,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(DataFlowType contentType |
|
exists(DataFlowType contentType |
|
||||||
fwdFlow(node1, cc, argAp, ap1, config) and
|
fwdFlow(node1, cc, argAp, ap1, config) and
|
||||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
|
||||||
typecheckStore(ap1, contentType)
|
typecheckStore(ap1, contentType)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1164,8 +1107,9 @@ private module Stage2 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1180,8 +1124,9 @@ private module Stage2 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,8 +1136,10 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,7 +1153,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p |
|
exists(ParamNodeEx p |
|
||||||
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
|
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
|
||||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1248,11 +1195,6 @@ private module Stage2 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -1273,7 +1215,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -1329,7 +1271,7 @@ private module Stage2 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -1364,8 +1306,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,8 +1318,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1386,8 +1330,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1447,7 +1392,7 @@ private module Stage2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -1671,8 +1616,6 @@ private module Stage3 {
|
|||||||
|
|
||||||
Cc ccNone() { result = false }
|
Cc ccNone() { result = false }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result = true }
|
|
||||||
|
|
||||||
private class LocalCc = Unit;
|
private class LocalCc = Unit;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -1727,14 +1670,12 @@ private module Stage3 {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1756,7 +1697,7 @@ private module Stage3 {
|
|||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -1859,8 +1800,9 @@ private module Stage3 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1875,8 +1817,9 @@ private module Stage3 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1886,8 +1829,10 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1943,11 +1888,6 @@ private module Stage3 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -1968,7 +1908,7 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -2024,7 +1964,7 @@ private module Stage3 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -2059,8 +1999,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2070,8 +2011,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2081,8 +2023,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2142,7 +2085,7 @@ private module Stage3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -2423,8 +2366,6 @@ private module Stage4 {
|
|||||||
|
|
||||||
Cc ccNone() { result instanceof CallContextAny }
|
Cc ccNone() { result instanceof CallContextAny }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
|
||||||
|
|
||||||
private class LocalCc = LocalCallContext;
|
private class LocalCc = LocalCallContext;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -2493,14 +2434,12 @@ private module Stage4 {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2522,7 +2461,7 @@ private module Stage4 {
|
|||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -2625,8 +2564,9 @@ private module Stage4 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2641,8 +2581,9 @@ private module Stage4 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2652,8 +2593,10 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2709,11 +2652,6 @@ private module Stage4 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -2734,7 +2672,7 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -2790,7 +2728,7 @@ private module Stage4 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -2825,8 +2763,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2836,8 +2775,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2847,8 +2787,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2908,7 +2849,7 @@ private module Stage4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -2992,7 +2933,7 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
|||||||
|
|
||||||
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
|
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
|
||||||
|
|
||||||
ParameterPosition getParameterPos() { p.isParameterOf(_, result) }
|
int getParameterPos() { p.isParameterOf(_, result) }
|
||||||
|
|
||||||
ParamNodeEx getParamNode() { result = p }
|
ParamNodeEx getParamNode() { result = p }
|
||||||
|
|
||||||
@@ -3123,11 +3064,7 @@ private newtype TPathNode =
|
|||||||
// A PathNode is introduced by a source ...
|
// A PathNode is introduced by a source ...
|
||||||
Stage4::revFlow(node, config) and
|
Stage4::revFlow(node, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(
|
cc instanceof CallContextAny and
|
||||||
if hasSourceCallCtx(config)
|
|
||||||
then cc instanceof CallContextSomeCall
|
|
||||||
else cc instanceof CallContextAny
|
|
||||||
) and
|
|
||||||
sc instanceof SummaryCtxNone and
|
sc instanceof SummaryCtxNone and
|
||||||
ap = TAccessPathNil(node.getDataFlowType())
|
ap = TAccessPathNil(node.getDataFlowType())
|
||||||
or
|
or
|
||||||
@@ -3139,10 +3076,17 @@ private newtype TPathNode =
|
|||||||
)
|
)
|
||||||
} or
|
} or
|
||||||
TPathNodeSink(NodeEx node, Configuration config) {
|
TPathNodeSink(NodeEx node, Configuration config) {
|
||||||
exists(PathNodeMid sink |
|
sinkNode(node, pragma[only_bind_into](config)) and
|
||||||
sink.isAtSink() and
|
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||||
node = sink.getNodeEx() and
|
(
|
||||||
config = sink.getConfiguration()
|
// A sink that is also a source ...
|
||||||
|
sourceNode(node, config)
|
||||||
|
or
|
||||||
|
// ... or a sink that can be reached from a source
|
||||||
|
exists(PathNodeMid mid |
|
||||||
|
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||||
|
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3459,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
|||||||
// an intermediate step to another intermediate node
|
// an intermediate step to another intermediate node
|
||||||
result = this.getSuccMid()
|
result = this.getSuccMid()
|
||||||
or
|
or
|
||||||
// a final step to a sink
|
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||||
result = this.getSuccMid().projectToSink()
|
exists(PathNodeMid mid, PathNodeSink sink |
|
||||||
|
mid = this.getSuccMid() and
|
||||||
|
mid.getNodeEx() = sink.getNodeEx() and
|
||||||
|
mid.getAp() instanceof AccessPathNil and
|
||||||
|
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||||
|
result = sink
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSource() {
|
override predicate isSource() {
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(
|
cc instanceof CallContextAny and
|
||||||
if hasSourceCallCtx(config)
|
|
||||||
then cc instanceof CallContextSomeCall
|
|
||||||
else cc instanceof CallContextAny
|
|
||||||
) and
|
|
||||||
sc instanceof SummaryCtxNone and
|
sc instanceof SummaryCtxNone and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate isAtSink() {
|
|
||||||
sinkNode(node, config) and
|
|
||||||
ap instanceof AccessPathNil and
|
|
||||||
if hasSinkCallCtx(config)
|
|
||||||
then
|
|
||||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
|
||||||
// is exactly what we need to check. This also implies
|
|
||||||
// `sc instanceof SummaryCtxNone`.
|
|
||||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
|
||||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
|
||||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
|
||||||
// in this case there's never any need to enter a call except to identify
|
|
||||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
|
||||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
|
||||||
// in the call context of the source.
|
|
||||||
sc instanceof SummaryCtxNone or
|
|
||||||
cc instanceof CallContextNoCall
|
|
||||||
else any()
|
|
||||||
}
|
|
||||||
|
|
||||||
PathNodeSink projectToSink() {
|
|
||||||
this.isAtSink() and
|
|
||||||
result.getNodeEx() = node and
|
|
||||||
result.getConfiguration() = unbindConf(config)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3639,40 +3559,39 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
|||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate pathIntoArg(
|
private predicate pathIntoArg(
|
||||||
PathNodeMid mid, ParameterPosition ppos, CallContext cc, DataFlowCall call, AccessPath ap,
|
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||||
AccessPathApprox apa, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ArgNode arg, ArgumentPosition apos |
|
exists(ArgNode arg |
|
||||||
arg = mid.getNodeEx().asNode() and
|
arg = mid.getNodeEx().asNode() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
arg.argumentOf(call, apos) and
|
arg.argumentOf(call, i) and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
apa = ap.getApprox() and
|
apa = ap.getApprox() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[noinline]
|
||||||
private predicate parameterCand(
|
private predicate parameterCand(
|
||||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p |
|
exists(ParamNodeEx p |
|
||||||
Stage4::revFlow(p, _, _, apa, config) and
|
Stage4::revFlow(p, _, _, apa, config) and
|
||||||
p.isParameterOf(callable, pos)
|
p.isParameterOf(callable, i)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate pathIntoCallable0(
|
private predicate pathIntoCallable0(
|
||||||
PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, CallContext outercc,
|
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||||
DataFlowCall call, AccessPath ap, Configuration config
|
AccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(AccessPathApprox apa |
|
exists(AccessPathApprox apa |
|
||||||
pathIntoArg(mid, pragma[only_bind_into](pos), outercc, call, ap, pragma[only_bind_into](apa),
|
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config)) and
|
||||||
callable = resolveCall(call, outercc) and
|
callable = resolveCall(call, outercc) and
|
||||||
parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa),
|
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||||
pragma[only_bind_into](config))
|
pragma[only_bind_into](config))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3687,18 +3606,14 @@ private predicate pathIntoCallable(
|
|||||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||||
DataFlowCall call, Configuration config
|
DataFlowCall call, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParameterPosition pos, DataFlowCallable callable, AccessPath ap |
|
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||||
pathIntoCallable0(mid, callable, pos, outercc, call, ap, config) and
|
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||||
p.isParameterOf(callable, pos) and
|
p.isParameterOf(callable, i) and
|
||||||
(
|
(
|
||||||
sc = TSummaryCtxSome(p, ap)
|
sc = TSummaryCtxSome(p, ap)
|
||||||
or
|
or
|
||||||
not exists(TSummaryCtxSome(p, ap)) and
|
not exists(TSummaryCtxSome(p, ap)) and
|
||||||
sc = TSummaryCtxNone() and
|
sc = TSummaryCtxNone()
|
||||||
// When the call contexts of source and sink needs to match then there's
|
|
||||||
// never any reason to enter a callable except to find a summary. See also
|
|
||||||
// the comment in `PathNodeMid::isAtSink`.
|
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
)
|
||||||
|
|
|
|
||||||
if recordDataFlowCallSite(call, callable)
|
if recordDataFlowCallSite(call, callable)
|
||||||
@@ -3713,7 +3628,7 @@ private predicate paramFlowsThrough(
|
|||||||
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
|
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
exists(PathNodeMid mid, RetNodeEx ret, int pos |
|
||||||
mid.getNodeEx() = ret and
|
mid.getNodeEx() = ret and
|
||||||
kind = ret.getKind() and
|
kind = ret.getKind() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
@@ -3761,14 +3676,13 @@ private module Subpaths {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate subpaths01(
|
private predicate subpaths01(
|
||||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||||
NodeEx out, AccessPath apout
|
NodeEx out, AccessPath apout
|
||||||
) {
|
) {
|
||||||
exists(Configuration config |
|
exists(Configuration config |
|
||||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) and
|
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||||
not arg.isHidden()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3802,17 +3716,8 @@ private module Subpaths {
|
|||||||
innercc = ret.getCallContext() and
|
innercc = ret.getCallContext() and
|
||||||
sc = ret.getSummaryCtx() and
|
sc = ret.getSummaryCtx() and
|
||||||
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
||||||
apout = ret.getAp()
|
apout = ret.getAp() and
|
||||||
)
|
not ret.isHidden()
|
||||||
}
|
|
||||||
|
|
||||||
private PathNodeImpl localStepToHidden(PathNodeImpl n) {
|
|
||||||
n.getASuccessorImpl() = result and
|
|
||||||
result.isHidden() and
|
|
||||||
exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() |
|
|
||||||
localFlowBigStep(n1, n2, _, _, _, _) or
|
|
||||||
store(n1, _, n2, _, _) or
|
|
||||||
read(n1, _, n2, _)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3821,12 +3726,11 @@ private module Subpaths {
|
|||||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||||
*/
|
*/
|
||||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNodeMid out) {
|
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||||
subpaths03(arg, p, localStepToHidden*(ret), o, apout) and
|
subpaths03(arg, p, ret, o, apout) and
|
||||||
not ret.isHidden() and
|
|
||||||
par.getNodeEx() = p and
|
par.getNodeEx() = p and
|
||||||
out.getNodeEx() = o and
|
out.getNodeEx() = o and
|
||||||
out.getAp() = apout
|
out.getAp() = apout
|
||||||
@@ -4442,25 +4346,24 @@ private module FlowExploration {
|
|||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate partialPathIntoArg(
|
private predicate partialPathIntoArg(
|
||||||
PartialPathNodeFwd mid, ParameterPosition ppos, CallContext cc, DataFlowCall call,
|
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||||
PartialAccessPath ap, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ArgNode arg, ArgumentPosition apos |
|
exists(ArgNode arg |
|
||||||
arg = mid.getNodeEx().asNode() and
|
arg = mid.getNodeEx().asNode() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
arg.argumentOf(call, apos) and
|
arg.argumentOf(call, i) and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate partialPathIntoCallable0(
|
private predicate partialPathIntoCallable0(
|
||||||
PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, CallContext outercc,
|
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
partialPathIntoArg(mid, pos, outercc, call, ap, config) and
|
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||||
callable = resolveCall(call, outercc)
|
callable = resolveCall(call, outercc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4469,9 +4372,9 @@ private module FlowExploration {
|
|||||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParameterPosition pos, DataFlowCallable callable |
|
exists(int i, DataFlowCallable callable |
|
||||||
partialPathIntoCallable0(mid, callable, pos, outercc, call, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||||
p.isParameterOf(callable, pos) and
|
p.isParameterOf(callable, i) and
|
||||||
sc1 = TSummaryCtx1Param(p) and
|
sc1 = TSummaryCtx1Param(p) and
|
||||||
sc2 = TSummaryCtx2Some(ap)
|
sc2 = TSummaryCtx2Some(ap)
|
||||||
|
|
|
|
||||||
@@ -4635,23 +4538,22 @@ private module FlowExploration {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate revPartialPathFlowsThrough(
|
private predicate revPartialPathFlowsThrough(
|
||||||
ArgumentPosition apos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||||
RevPartialAccessPath ap, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos |
|
exists(PartialPathNodeRev mid, ParamNodeEx p |
|
||||||
mid.getNodeEx() = p and
|
mid.getNodeEx() = p and
|
||||||
p.getPosition() = ppos and
|
p.getPosition() = pos and
|
||||||
sc1 = mid.getSummaryCtx1() and
|
sc1 = mid.getSummaryCtx1() and
|
||||||
sc2 = mid.getSummaryCtx2() and
|
sc2 = mid.getSummaryCtx2() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate revPartialPathThroughCallable0(
|
private predicate revPartialPathThroughCallable0(
|
||||||
DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, RevPartialAccessPath ap,
|
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||||
@@ -4664,7 +4566,7 @@ private module FlowExploration {
|
|||||||
private predicate revPartialPathThroughCallable(
|
private predicate revPartialPathThroughCallable(
|
||||||
PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
|
PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(DataFlowCall call, ArgumentPosition pos |
|
exists(DataFlowCall call, int pos |
|
||||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||||
node.asNode().(ArgNode).argumentOf(call, pos)
|
node.asNode().(ArgNode).argumentOf(call, pos)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
import DataFlowImplCommonPublic
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A configuration of interprocedural data flow analysis. This defines
|
* A configuration of interprocedural data flow analysis. This defines
|
||||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
int fieldFlowBranchLimit() { result = 2 }
|
int fieldFlowBranchLimit() { result = 2 }
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a data flow configuration feature to add restrictions to the set of
|
|
||||||
* valid flow paths.
|
|
||||||
*
|
|
||||||
* - `FeatureHasSourceCallContext`:
|
|
||||||
* Assume that sources have some existing call context to disallow
|
|
||||||
* conflicting return-flow directly following the source.
|
|
||||||
* - `FeatureHasSinkCallContext`:
|
|
||||||
* Assume that sinks have some existing call context to disallow
|
|
||||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
|
||||||
* - `FeatureEqualSourceSinkCallContext`:
|
|
||||||
* Implies both of the above and additionally ensures that the entire flow
|
|
||||||
* path preserves the call context.
|
|
||||||
*/
|
|
||||||
FlowFeature getAFeature() { none() }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||||
*/
|
*/
|
||||||
@@ -256,11 +239,11 @@ private class ArgNodeEx extends NodeEx {
|
|||||||
private class ParamNodeEx extends NodeEx {
|
private class ParamNodeEx extends NodeEx {
|
||||||
ParamNodeEx() { this.asNode() instanceof ParamNode }
|
ParamNodeEx() { this.asNode() instanceof ParamNode }
|
||||||
|
|
||||||
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
predicate isParameterOf(DataFlowCallable c, int i) {
|
||||||
this.asNode().(ParamNode).isParameterOf(c, pos)
|
this.asNode().(ParamNode).isParameterOf(c, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
int getPosition() { this.isParameterOf(_, result) }
|
||||||
|
|
||||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||||
}
|
}
|
||||||
@@ -289,7 +272,6 @@ private predicate outBarrier(NodeEx node, Configuration config) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate fullBarrier(NodeEx node, Configuration config) {
|
private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||||
exists(Node n | node.asNode() = n |
|
exists(Node n | node.asNode() = n |
|
||||||
config.isBarrier(n)
|
config.isBarrier(n)
|
||||||
@@ -308,23 +290,11 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate sourceNode(NodeEx node, Configuration config) {
|
private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
|
||||||
config.isSource(node.asNode()) and
|
|
||||||
not fullBarrier(node, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
|
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
|
||||||
|
|
||||||
/** Provides the relevant barriers for a step from `node1` to `node2`. */
|
|
||||||
pragma[inline]
|
|
||||||
private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) {
|
|
||||||
not outBarrier(node1, config) and
|
|
||||||
not inBarrier(node2, config) and
|
|
||||||
not fullBarrier(node1, config) and
|
|
||||||
not fullBarrier(node2, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||||
*/
|
*/
|
||||||
@@ -333,14 +303,16 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
|
|||||||
node1.asNode() = n1 and
|
node1.asNode() = n1 and
|
||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
simpleLocalFlowStepExt(n1, n2) and
|
simpleLocalFlowStepExt(n1, n2) and
|
||||||
stepFilter(node1, node2, config)
|
not outBarrier(node1, config) and
|
||||||
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
config.allowImplicitRead(n, _) and
|
config.allowImplicitRead(n, _) and
|
||||||
node1.asNode() = n and
|
node1.asNode() = n and
|
||||||
node2.isImplicitReadNode(n, false) and
|
node2.isImplicitReadNode(n, false)
|
||||||
not fullBarrier(node1, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,14 +325,16 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
|
|||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
config.isAdditionalFlowStep(n1, n2) and
|
config.isAdditionalFlowStep(n1, n2) and
|
||||||
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
|
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
|
||||||
stepFilter(node1, node2, config)
|
not outBarrier(node1, config) and
|
||||||
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
config.allowImplicitRead(n, _) and
|
config.allowImplicitRead(n, _) and
|
||||||
node1.isImplicitReadNode(n, true) and
|
node1.isImplicitReadNode(n, true) and
|
||||||
node2.asNode() = n and
|
node2.asNode() = n
|
||||||
not fullBarrier(node2, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,8 +346,10 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
|||||||
node1.asNode() = n1 and
|
node1.asNode() = n1 and
|
||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
jumpStepCached(n1, n2) and
|
jumpStepCached(n1, n2) and
|
||||||
stepFilter(node1, node2, config) and
|
not outBarrier(node1, config) and
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,14 +362,15 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
|||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
config.isAdditionalFlowStep(n1, n2) and
|
config.isAdditionalFlowStep(n1, n2) and
|
||||||
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
|
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
|
||||||
stepFilter(node1, node2, config) and
|
not outBarrier(node1, config) and
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||||
read(node1.asNode(), c, node2.asNode()) and
|
read(node1.asNode(), c, node2.asNode())
|
||||||
stepFilter(node1, node2, config)
|
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
node2.isImplicitReadNode(n, true) and
|
node2.isImplicitReadNode(n, true) and
|
||||||
@@ -406,8 +383,7 @@ private predicate store(
|
|||||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||||
) {
|
) {
|
||||||
store(node1.asNode(), tc, node2.asNode(), contentType) and
|
store(node1.asNode(), tc, node2.asNode(), contentType) and
|
||||||
read(_, tc.getContent(), _, config) and
|
read(_, tc.getContent(), _, config)
|
||||||
stepFilter(node1, node2, config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
@@ -425,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
|||||||
*/
|
*/
|
||||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||||
|
|
||||||
private predicate hasSourceCallCtx(Configuration config) {
|
|
||||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
|
||||||
feature instanceof FeatureHasSourceCallContext or
|
|
||||||
feature instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate hasSinkCallCtx(Configuration config) {
|
|
||||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
|
||||||
feature instanceof FeatureHasSinkCallContext or
|
|
||||||
feature instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private module Stage1 {
|
private module Stage1 {
|
||||||
class ApApprox = Unit;
|
class ApApprox = Unit;
|
||||||
|
|
||||||
@@ -456,8 +418,10 @@ private module Stage1 {
|
|||||||
* argument in a call.
|
* argument in a call.
|
||||||
*/
|
*/
|
||||||
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||||
|
not fullBarrier(node, config) and
|
||||||
|
(
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
cc = false
|
||||||
or
|
or
|
||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
fwdFlow(mid, cc, config) and
|
fwdFlow(mid, cc, config) and
|
||||||
@@ -485,21 +449,22 @@ private module Stage1 {
|
|||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
useFieldFlow(config) and
|
useFieldFlow(config) and
|
||||||
fwdFlow(mid, cc, config) and
|
fwdFlow(mid, cc, config) and
|
||||||
store(mid, _, node, _, config)
|
store(mid, _, node, _, config) and
|
||||||
|
not outBarrier(mid, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// read
|
// read
|
||||||
exists(Content c |
|
exists(Content c |
|
||||||
fwdFlowRead(c, node, cc, config) and
|
fwdFlowRead(c, node, cc, config) and
|
||||||
fwdFlowConsCand(c, config)
|
fwdFlowConsCand(c, config) and
|
||||||
|
not inBarrier(node, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow into a callable
|
// flow into a callable
|
||||||
exists(NodeEx arg |
|
exists(NodeEx arg |
|
||||||
fwdFlow(arg, _, config) and
|
fwdFlow(arg, _, config) and
|
||||||
viableParamArgEx(_, node, arg) and
|
viableParamArgEx(_, node, arg) and
|
||||||
cc = true and
|
cc = true
|
||||||
not fullBarrier(node, config)
|
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
@@ -510,6 +475,7 @@ private module Stage1 {
|
|||||||
fwdFlowOutFromArg(call, node, config) and
|
fwdFlowOutFromArg(call, node, config) and
|
||||||
fwdFlowIsEntered(call, cc, config)
|
fwdFlowIsEntered(call, cc, config)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||||
@@ -548,8 +514,7 @@ private module Stage1 {
|
|||||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||||
exists(ReturnPosition pos |
|
exists(ReturnPosition pos |
|
||||||
fwdFlowReturnPosition(pos, cc, config) and
|
fwdFlowReturnPosition(pos, cc, config) and
|
||||||
viableReturnPosOutEx(call, pos, out) and
|
viableReturnPosOutEx(call, pos, out)
|
||||||
not fullBarrier(out, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +551,7 @@ private module Stage1 {
|
|||||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||||
fwdFlow(node, config) and
|
fwdFlow(node, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
toReturn = false
|
||||||
or
|
or
|
||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
localFlowStep(node, mid, config) and
|
localFlowStep(node, mid, config) and
|
||||||
@@ -775,7 +740,6 @@ private module Stage1 {
|
|||||||
* Holds if flow may enter through `p` and reach a return node making `p` a
|
* Holds if flow may enter through `p` and reach a return node making `p` a
|
||||||
* candidate for the origin of a summary.
|
* candidate for the origin of a summary.
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(ReturnKindExt kind |
|
exists(ReturnKindExt kind |
|
||||||
throughFlowNodeCand(p, config) and
|
throughFlowNodeCand(p, config) and
|
||||||
@@ -973,8 +937,6 @@ private module Stage2 {
|
|||||||
|
|
||||||
Cc ccNone() { result instanceof CallContextAny }
|
Cc ccNone() { result instanceof CallContextAny }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
|
||||||
|
|
||||||
private class LocalCc = Unit;
|
private class LocalCc = Unit;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -1012,9 +974,6 @@ private module Stage2 {
|
|||||||
|
|
||||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||||
|
|
||||||
bindingset[node, ap]
|
|
||||||
private predicate filter(NodeEx node, Ap ap) { any() }
|
|
||||||
|
|
||||||
bindingset[ap, contentType]
|
bindingset[ap, contentType]
|
||||||
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||||
|
|
||||||
@@ -1023,23 +982,14 @@ private module Stage2 {
|
|||||||
PrevStage::revFlow(node, _, _, apa, config)
|
PrevStage::revFlow(node, _, _, apa, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingset[result, apa]
|
|
||||||
private ApApprox unbindApa(ApApprox apa) {
|
|
||||||
exists(ApApprox apa0 |
|
|
||||||
apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1052,16 +1002,9 @@ private module Stage2 {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
fwdFlow0(node, cc, argAp, ap, config) and
|
|
||||||
flowCand(node, unbindApa(getApprox(ap)), config) and
|
|
||||||
filter(node, ap)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -1129,7 +1072,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(DataFlowType contentType |
|
exists(DataFlowType contentType |
|
||||||
fwdFlow(node1, cc, argAp, ap1, config) and
|
fwdFlow(node1, cc, argAp, ap1, config) and
|
||||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
|
||||||
typecheckStore(ap1, contentType)
|
typecheckStore(ap1, contentType)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1164,8 +1107,9 @@ private module Stage2 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1180,8 +1124,9 @@ private module Stage2 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,8 +1136,10 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,7 +1153,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p |
|
exists(ParamNodeEx p |
|
||||||
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
|
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
|
||||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1248,11 +1195,6 @@ private module Stage2 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -1273,7 +1215,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -1329,7 +1271,7 @@ private module Stage2 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -1364,8 +1306,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,8 +1318,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1386,8 +1330,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1447,7 +1392,7 @@ private module Stage2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -1671,8 +1616,6 @@ private module Stage3 {
|
|||||||
|
|
||||||
Cc ccNone() { result = false }
|
Cc ccNone() { result = false }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result = true }
|
|
||||||
|
|
||||||
private class LocalCc = Unit;
|
private class LocalCc = Unit;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -1727,14 +1670,12 @@ private module Stage3 {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1756,7 +1697,7 @@ private module Stage3 {
|
|||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -1859,8 +1800,9 @@ private module Stage3 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1875,8 +1817,9 @@ private module Stage3 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1886,8 +1829,10 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1943,11 +1888,6 @@ private module Stage3 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -1968,7 +1908,7 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -2024,7 +1964,7 @@ private module Stage3 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -2059,8 +1999,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2070,8 +2011,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2081,8 +2023,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2142,7 +2085,7 @@ private module Stage3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -2423,8 +2366,6 @@ private module Stage4 {
|
|||||||
|
|
||||||
Cc ccNone() { result instanceof CallContextAny }
|
Cc ccNone() { result instanceof CallContextAny }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
|
||||||
|
|
||||||
private class LocalCc = LocalCallContext;
|
private class LocalCc = LocalCallContext;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -2493,14 +2434,12 @@ private module Stage4 {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2522,7 +2461,7 @@ private module Stage4 {
|
|||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -2625,8 +2564,9 @@ private module Stage4 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2641,8 +2581,9 @@ private module Stage4 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2652,8 +2593,10 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2709,11 +2652,6 @@ private module Stage4 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -2734,7 +2672,7 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -2790,7 +2728,7 @@ private module Stage4 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -2825,8 +2763,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2836,8 +2775,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2847,8 +2787,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2908,7 +2849,7 @@ private module Stage4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -2992,7 +2933,7 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
|||||||
|
|
||||||
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
|
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
|
||||||
|
|
||||||
ParameterPosition getParameterPos() { p.isParameterOf(_, result) }
|
int getParameterPos() { p.isParameterOf(_, result) }
|
||||||
|
|
||||||
ParamNodeEx getParamNode() { result = p }
|
ParamNodeEx getParamNode() { result = p }
|
||||||
|
|
||||||
@@ -3123,11 +3064,7 @@ private newtype TPathNode =
|
|||||||
// A PathNode is introduced by a source ...
|
// A PathNode is introduced by a source ...
|
||||||
Stage4::revFlow(node, config) and
|
Stage4::revFlow(node, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(
|
cc instanceof CallContextAny and
|
||||||
if hasSourceCallCtx(config)
|
|
||||||
then cc instanceof CallContextSomeCall
|
|
||||||
else cc instanceof CallContextAny
|
|
||||||
) and
|
|
||||||
sc instanceof SummaryCtxNone and
|
sc instanceof SummaryCtxNone and
|
||||||
ap = TAccessPathNil(node.getDataFlowType())
|
ap = TAccessPathNil(node.getDataFlowType())
|
||||||
or
|
or
|
||||||
@@ -3139,10 +3076,17 @@ private newtype TPathNode =
|
|||||||
)
|
)
|
||||||
} or
|
} or
|
||||||
TPathNodeSink(NodeEx node, Configuration config) {
|
TPathNodeSink(NodeEx node, Configuration config) {
|
||||||
exists(PathNodeMid sink |
|
sinkNode(node, pragma[only_bind_into](config)) and
|
||||||
sink.isAtSink() and
|
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||||
node = sink.getNodeEx() and
|
(
|
||||||
config = sink.getConfiguration()
|
// A sink that is also a source ...
|
||||||
|
sourceNode(node, config)
|
||||||
|
or
|
||||||
|
// ... or a sink that can be reached from a source
|
||||||
|
exists(PathNodeMid mid |
|
||||||
|
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||||
|
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3459,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
|||||||
// an intermediate step to another intermediate node
|
// an intermediate step to another intermediate node
|
||||||
result = this.getSuccMid()
|
result = this.getSuccMid()
|
||||||
or
|
or
|
||||||
// a final step to a sink
|
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||||
result = this.getSuccMid().projectToSink()
|
exists(PathNodeMid mid, PathNodeSink sink |
|
||||||
|
mid = this.getSuccMid() and
|
||||||
|
mid.getNodeEx() = sink.getNodeEx() and
|
||||||
|
mid.getAp() instanceof AccessPathNil and
|
||||||
|
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||||
|
result = sink
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSource() {
|
override predicate isSource() {
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(
|
cc instanceof CallContextAny and
|
||||||
if hasSourceCallCtx(config)
|
|
||||||
then cc instanceof CallContextSomeCall
|
|
||||||
else cc instanceof CallContextAny
|
|
||||||
) and
|
|
||||||
sc instanceof SummaryCtxNone and
|
sc instanceof SummaryCtxNone and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate isAtSink() {
|
|
||||||
sinkNode(node, config) and
|
|
||||||
ap instanceof AccessPathNil and
|
|
||||||
if hasSinkCallCtx(config)
|
|
||||||
then
|
|
||||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
|
||||||
// is exactly what we need to check. This also implies
|
|
||||||
// `sc instanceof SummaryCtxNone`.
|
|
||||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
|
||||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
|
||||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
|
||||||
// in this case there's never any need to enter a call except to identify
|
|
||||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
|
||||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
|
||||||
// in the call context of the source.
|
|
||||||
sc instanceof SummaryCtxNone or
|
|
||||||
cc instanceof CallContextNoCall
|
|
||||||
else any()
|
|
||||||
}
|
|
||||||
|
|
||||||
PathNodeSink projectToSink() {
|
|
||||||
this.isAtSink() and
|
|
||||||
result.getNodeEx() = node and
|
|
||||||
result.getConfiguration() = unbindConf(config)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3639,40 +3559,39 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
|||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate pathIntoArg(
|
private predicate pathIntoArg(
|
||||||
PathNodeMid mid, ParameterPosition ppos, CallContext cc, DataFlowCall call, AccessPath ap,
|
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||||
AccessPathApprox apa, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ArgNode arg, ArgumentPosition apos |
|
exists(ArgNode arg |
|
||||||
arg = mid.getNodeEx().asNode() and
|
arg = mid.getNodeEx().asNode() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
arg.argumentOf(call, apos) and
|
arg.argumentOf(call, i) and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
apa = ap.getApprox() and
|
apa = ap.getApprox() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[noinline]
|
||||||
private predicate parameterCand(
|
private predicate parameterCand(
|
||||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p |
|
exists(ParamNodeEx p |
|
||||||
Stage4::revFlow(p, _, _, apa, config) and
|
Stage4::revFlow(p, _, _, apa, config) and
|
||||||
p.isParameterOf(callable, pos)
|
p.isParameterOf(callable, i)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate pathIntoCallable0(
|
private predicate pathIntoCallable0(
|
||||||
PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, CallContext outercc,
|
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||||
DataFlowCall call, AccessPath ap, Configuration config
|
AccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(AccessPathApprox apa |
|
exists(AccessPathApprox apa |
|
||||||
pathIntoArg(mid, pragma[only_bind_into](pos), outercc, call, ap, pragma[only_bind_into](apa),
|
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config)) and
|
||||||
callable = resolveCall(call, outercc) and
|
callable = resolveCall(call, outercc) and
|
||||||
parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa),
|
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||||
pragma[only_bind_into](config))
|
pragma[only_bind_into](config))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3687,18 +3606,14 @@ private predicate pathIntoCallable(
|
|||||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||||
DataFlowCall call, Configuration config
|
DataFlowCall call, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParameterPosition pos, DataFlowCallable callable, AccessPath ap |
|
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||||
pathIntoCallable0(mid, callable, pos, outercc, call, ap, config) and
|
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||||
p.isParameterOf(callable, pos) and
|
p.isParameterOf(callable, i) and
|
||||||
(
|
(
|
||||||
sc = TSummaryCtxSome(p, ap)
|
sc = TSummaryCtxSome(p, ap)
|
||||||
or
|
or
|
||||||
not exists(TSummaryCtxSome(p, ap)) and
|
not exists(TSummaryCtxSome(p, ap)) and
|
||||||
sc = TSummaryCtxNone() and
|
sc = TSummaryCtxNone()
|
||||||
// When the call contexts of source and sink needs to match then there's
|
|
||||||
// never any reason to enter a callable except to find a summary. See also
|
|
||||||
// the comment in `PathNodeMid::isAtSink`.
|
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
)
|
||||||
|
|
|
|
||||||
if recordDataFlowCallSite(call, callable)
|
if recordDataFlowCallSite(call, callable)
|
||||||
@@ -3713,7 +3628,7 @@ private predicate paramFlowsThrough(
|
|||||||
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
|
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
exists(PathNodeMid mid, RetNodeEx ret, int pos |
|
||||||
mid.getNodeEx() = ret and
|
mid.getNodeEx() = ret and
|
||||||
kind = ret.getKind() and
|
kind = ret.getKind() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
@@ -3761,14 +3676,13 @@ private module Subpaths {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate subpaths01(
|
private predicate subpaths01(
|
||||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||||
NodeEx out, AccessPath apout
|
NodeEx out, AccessPath apout
|
||||||
) {
|
) {
|
||||||
exists(Configuration config |
|
exists(Configuration config |
|
||||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) and
|
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||||
not arg.isHidden()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3802,17 +3716,8 @@ private module Subpaths {
|
|||||||
innercc = ret.getCallContext() and
|
innercc = ret.getCallContext() and
|
||||||
sc = ret.getSummaryCtx() and
|
sc = ret.getSummaryCtx() and
|
||||||
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
||||||
apout = ret.getAp()
|
apout = ret.getAp() and
|
||||||
)
|
not ret.isHidden()
|
||||||
}
|
|
||||||
|
|
||||||
private PathNodeImpl localStepToHidden(PathNodeImpl n) {
|
|
||||||
n.getASuccessorImpl() = result and
|
|
||||||
result.isHidden() and
|
|
||||||
exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() |
|
|
||||||
localFlowBigStep(n1, n2, _, _, _, _) or
|
|
||||||
store(n1, _, n2, _, _) or
|
|
||||||
read(n1, _, n2, _)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3821,12 +3726,11 @@ private module Subpaths {
|
|||||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||||
*/
|
*/
|
||||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNodeMid out) {
|
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||||
subpaths03(arg, p, localStepToHidden*(ret), o, apout) and
|
subpaths03(arg, p, ret, o, apout) and
|
||||||
not ret.isHidden() and
|
|
||||||
par.getNodeEx() = p and
|
par.getNodeEx() = p and
|
||||||
out.getNodeEx() = o and
|
out.getNodeEx() = o and
|
||||||
out.getAp() = apout
|
out.getAp() = apout
|
||||||
@@ -4442,25 +4346,24 @@ private module FlowExploration {
|
|||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate partialPathIntoArg(
|
private predicate partialPathIntoArg(
|
||||||
PartialPathNodeFwd mid, ParameterPosition ppos, CallContext cc, DataFlowCall call,
|
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||||
PartialAccessPath ap, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ArgNode arg, ArgumentPosition apos |
|
exists(ArgNode arg |
|
||||||
arg = mid.getNodeEx().asNode() and
|
arg = mid.getNodeEx().asNode() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
arg.argumentOf(call, apos) and
|
arg.argumentOf(call, i) and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate partialPathIntoCallable0(
|
private predicate partialPathIntoCallable0(
|
||||||
PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, CallContext outercc,
|
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
partialPathIntoArg(mid, pos, outercc, call, ap, config) and
|
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||||
callable = resolveCall(call, outercc)
|
callable = resolveCall(call, outercc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4469,9 +4372,9 @@ private module FlowExploration {
|
|||||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParameterPosition pos, DataFlowCallable callable |
|
exists(int i, DataFlowCallable callable |
|
||||||
partialPathIntoCallable0(mid, callable, pos, outercc, call, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||||
p.isParameterOf(callable, pos) and
|
p.isParameterOf(callable, i) and
|
||||||
sc1 = TSummaryCtx1Param(p) and
|
sc1 = TSummaryCtx1Param(p) and
|
||||||
sc2 = TSummaryCtx2Some(ap)
|
sc2 = TSummaryCtx2Some(ap)
|
||||||
|
|
|
|
||||||
@@ -4635,23 +4538,22 @@ private module FlowExploration {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate revPartialPathFlowsThrough(
|
private predicate revPartialPathFlowsThrough(
|
||||||
ArgumentPosition apos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||||
RevPartialAccessPath ap, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos |
|
exists(PartialPathNodeRev mid, ParamNodeEx p |
|
||||||
mid.getNodeEx() = p and
|
mid.getNodeEx() = p and
|
||||||
p.getPosition() = ppos and
|
p.getPosition() = pos and
|
||||||
sc1 = mid.getSummaryCtx1() and
|
sc1 = mid.getSummaryCtx1() and
|
||||||
sc2 = mid.getSummaryCtx2() and
|
sc2 = mid.getSummaryCtx2() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate revPartialPathThroughCallable0(
|
private predicate revPartialPathThroughCallable0(
|
||||||
DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, RevPartialAccessPath ap,
|
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||||
@@ -4664,7 +4566,7 @@ private module FlowExploration {
|
|||||||
private predicate revPartialPathThroughCallable(
|
private predicate revPartialPathThroughCallable(
|
||||||
PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
|
PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(DataFlowCall call, ArgumentPosition pos |
|
exists(DataFlowCall call, int pos |
|
||||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||||
node.asNode().(ArgNode).argumentOf(call, pos)
|
node.asNode().(ArgNode).argumentOf(call, pos)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
import DataFlowImplCommonPublic
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A configuration of interprocedural data flow analysis. This defines
|
* A configuration of interprocedural data flow analysis. This defines
|
||||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
int fieldFlowBranchLimit() { result = 2 }
|
int fieldFlowBranchLimit() { result = 2 }
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a data flow configuration feature to add restrictions to the set of
|
|
||||||
* valid flow paths.
|
|
||||||
*
|
|
||||||
* - `FeatureHasSourceCallContext`:
|
|
||||||
* Assume that sources have some existing call context to disallow
|
|
||||||
* conflicting return-flow directly following the source.
|
|
||||||
* - `FeatureHasSinkCallContext`:
|
|
||||||
* Assume that sinks have some existing call context to disallow
|
|
||||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
|
||||||
* - `FeatureEqualSourceSinkCallContext`:
|
|
||||||
* Implies both of the above and additionally ensures that the entire flow
|
|
||||||
* path preserves the call context.
|
|
||||||
*/
|
|
||||||
FlowFeature getAFeature() { none() }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||||
*/
|
*/
|
||||||
@@ -256,11 +239,11 @@ private class ArgNodeEx extends NodeEx {
|
|||||||
private class ParamNodeEx extends NodeEx {
|
private class ParamNodeEx extends NodeEx {
|
||||||
ParamNodeEx() { this.asNode() instanceof ParamNode }
|
ParamNodeEx() { this.asNode() instanceof ParamNode }
|
||||||
|
|
||||||
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
predicate isParameterOf(DataFlowCallable c, int i) {
|
||||||
this.asNode().(ParamNode).isParameterOf(c, pos)
|
this.asNode().(ParamNode).isParameterOf(c, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
int getPosition() { this.isParameterOf(_, result) }
|
||||||
|
|
||||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||||
}
|
}
|
||||||
@@ -289,7 +272,6 @@ private predicate outBarrier(NodeEx node, Configuration config) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate fullBarrier(NodeEx node, Configuration config) {
|
private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||||
exists(Node n | node.asNode() = n |
|
exists(Node n | node.asNode() = n |
|
||||||
config.isBarrier(n)
|
config.isBarrier(n)
|
||||||
@@ -308,23 +290,11 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate sourceNode(NodeEx node, Configuration config) {
|
private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
|
||||||
config.isSource(node.asNode()) and
|
|
||||||
not fullBarrier(node, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
|
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
|
||||||
|
|
||||||
/** Provides the relevant barriers for a step from `node1` to `node2`. */
|
|
||||||
pragma[inline]
|
|
||||||
private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) {
|
|
||||||
not outBarrier(node1, config) and
|
|
||||||
not inBarrier(node2, config) and
|
|
||||||
not fullBarrier(node1, config) and
|
|
||||||
not fullBarrier(node2, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||||
*/
|
*/
|
||||||
@@ -333,14 +303,16 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
|
|||||||
node1.asNode() = n1 and
|
node1.asNode() = n1 and
|
||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
simpleLocalFlowStepExt(n1, n2) and
|
simpleLocalFlowStepExt(n1, n2) and
|
||||||
stepFilter(node1, node2, config)
|
not outBarrier(node1, config) and
|
||||||
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
config.allowImplicitRead(n, _) and
|
config.allowImplicitRead(n, _) and
|
||||||
node1.asNode() = n and
|
node1.asNode() = n and
|
||||||
node2.isImplicitReadNode(n, false) and
|
node2.isImplicitReadNode(n, false)
|
||||||
not fullBarrier(node1, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,14 +325,16 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
|
|||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
config.isAdditionalFlowStep(n1, n2) and
|
config.isAdditionalFlowStep(n1, n2) and
|
||||||
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
|
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
|
||||||
stepFilter(node1, node2, config)
|
not outBarrier(node1, config) and
|
||||||
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
config.allowImplicitRead(n, _) and
|
config.allowImplicitRead(n, _) and
|
||||||
node1.isImplicitReadNode(n, true) and
|
node1.isImplicitReadNode(n, true) and
|
||||||
node2.asNode() = n and
|
node2.asNode() = n
|
||||||
not fullBarrier(node2, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,8 +346,10 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
|||||||
node1.asNode() = n1 and
|
node1.asNode() = n1 and
|
||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
jumpStepCached(n1, n2) and
|
jumpStepCached(n1, n2) and
|
||||||
stepFilter(node1, node2, config) and
|
not outBarrier(node1, config) and
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,14 +362,15 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
|||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
config.isAdditionalFlowStep(n1, n2) and
|
config.isAdditionalFlowStep(n1, n2) and
|
||||||
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
|
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
|
||||||
stepFilter(node1, node2, config) and
|
not outBarrier(node1, config) and
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||||
read(node1.asNode(), c, node2.asNode()) and
|
read(node1.asNode(), c, node2.asNode())
|
||||||
stepFilter(node1, node2, config)
|
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
node2.isImplicitReadNode(n, true) and
|
node2.isImplicitReadNode(n, true) and
|
||||||
@@ -406,8 +383,7 @@ private predicate store(
|
|||||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||||
) {
|
) {
|
||||||
store(node1.asNode(), tc, node2.asNode(), contentType) and
|
store(node1.asNode(), tc, node2.asNode(), contentType) and
|
||||||
read(_, tc.getContent(), _, config) and
|
read(_, tc.getContent(), _, config)
|
||||||
stepFilter(node1, node2, config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
@@ -425,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
|||||||
*/
|
*/
|
||||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||||
|
|
||||||
private predicate hasSourceCallCtx(Configuration config) {
|
|
||||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
|
||||||
feature instanceof FeatureHasSourceCallContext or
|
|
||||||
feature instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate hasSinkCallCtx(Configuration config) {
|
|
||||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
|
||||||
feature instanceof FeatureHasSinkCallContext or
|
|
||||||
feature instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private module Stage1 {
|
private module Stage1 {
|
||||||
class ApApprox = Unit;
|
class ApApprox = Unit;
|
||||||
|
|
||||||
@@ -456,8 +418,10 @@ private module Stage1 {
|
|||||||
* argument in a call.
|
* argument in a call.
|
||||||
*/
|
*/
|
||||||
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||||
|
not fullBarrier(node, config) and
|
||||||
|
(
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
cc = false
|
||||||
or
|
or
|
||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
fwdFlow(mid, cc, config) and
|
fwdFlow(mid, cc, config) and
|
||||||
@@ -485,21 +449,22 @@ private module Stage1 {
|
|||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
useFieldFlow(config) and
|
useFieldFlow(config) and
|
||||||
fwdFlow(mid, cc, config) and
|
fwdFlow(mid, cc, config) and
|
||||||
store(mid, _, node, _, config)
|
store(mid, _, node, _, config) and
|
||||||
|
not outBarrier(mid, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// read
|
// read
|
||||||
exists(Content c |
|
exists(Content c |
|
||||||
fwdFlowRead(c, node, cc, config) and
|
fwdFlowRead(c, node, cc, config) and
|
||||||
fwdFlowConsCand(c, config)
|
fwdFlowConsCand(c, config) and
|
||||||
|
not inBarrier(node, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow into a callable
|
// flow into a callable
|
||||||
exists(NodeEx arg |
|
exists(NodeEx arg |
|
||||||
fwdFlow(arg, _, config) and
|
fwdFlow(arg, _, config) and
|
||||||
viableParamArgEx(_, node, arg) and
|
viableParamArgEx(_, node, arg) and
|
||||||
cc = true and
|
cc = true
|
||||||
not fullBarrier(node, config)
|
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
@@ -510,6 +475,7 @@ private module Stage1 {
|
|||||||
fwdFlowOutFromArg(call, node, config) and
|
fwdFlowOutFromArg(call, node, config) and
|
||||||
fwdFlowIsEntered(call, cc, config)
|
fwdFlowIsEntered(call, cc, config)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||||
@@ -548,8 +514,7 @@ private module Stage1 {
|
|||||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||||
exists(ReturnPosition pos |
|
exists(ReturnPosition pos |
|
||||||
fwdFlowReturnPosition(pos, cc, config) and
|
fwdFlowReturnPosition(pos, cc, config) and
|
||||||
viableReturnPosOutEx(call, pos, out) and
|
viableReturnPosOutEx(call, pos, out)
|
||||||
not fullBarrier(out, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +551,7 @@ private module Stage1 {
|
|||||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||||
fwdFlow(node, config) and
|
fwdFlow(node, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
toReturn = false
|
||||||
or
|
or
|
||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
localFlowStep(node, mid, config) and
|
localFlowStep(node, mid, config) and
|
||||||
@@ -775,7 +740,6 @@ private module Stage1 {
|
|||||||
* Holds if flow may enter through `p` and reach a return node making `p` a
|
* Holds if flow may enter through `p` and reach a return node making `p` a
|
||||||
* candidate for the origin of a summary.
|
* candidate for the origin of a summary.
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(ReturnKindExt kind |
|
exists(ReturnKindExt kind |
|
||||||
throughFlowNodeCand(p, config) and
|
throughFlowNodeCand(p, config) and
|
||||||
@@ -973,8 +937,6 @@ private module Stage2 {
|
|||||||
|
|
||||||
Cc ccNone() { result instanceof CallContextAny }
|
Cc ccNone() { result instanceof CallContextAny }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
|
||||||
|
|
||||||
private class LocalCc = Unit;
|
private class LocalCc = Unit;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -1012,9 +974,6 @@ private module Stage2 {
|
|||||||
|
|
||||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||||
|
|
||||||
bindingset[node, ap]
|
|
||||||
private predicate filter(NodeEx node, Ap ap) { any() }
|
|
||||||
|
|
||||||
bindingset[ap, contentType]
|
bindingset[ap, contentType]
|
||||||
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||||
|
|
||||||
@@ -1023,23 +982,14 @@ private module Stage2 {
|
|||||||
PrevStage::revFlow(node, _, _, apa, config)
|
PrevStage::revFlow(node, _, _, apa, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingset[result, apa]
|
|
||||||
private ApApprox unbindApa(ApApprox apa) {
|
|
||||||
exists(ApApprox apa0 |
|
|
||||||
apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1052,16 +1002,9 @@ private module Stage2 {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
fwdFlow0(node, cc, argAp, ap, config) and
|
|
||||||
flowCand(node, unbindApa(getApprox(ap)), config) and
|
|
||||||
filter(node, ap)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -1129,7 +1072,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(DataFlowType contentType |
|
exists(DataFlowType contentType |
|
||||||
fwdFlow(node1, cc, argAp, ap1, config) and
|
fwdFlow(node1, cc, argAp, ap1, config) and
|
||||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
|
||||||
typecheckStore(ap1, contentType)
|
typecheckStore(ap1, contentType)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1164,8 +1107,9 @@ private module Stage2 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1180,8 +1124,9 @@ private module Stage2 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,8 +1136,10 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,7 +1153,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p |
|
exists(ParamNodeEx p |
|
||||||
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
|
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
|
||||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1248,11 +1195,6 @@ private module Stage2 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -1273,7 +1215,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -1329,7 +1271,7 @@ private module Stage2 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -1364,8 +1306,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,8 +1318,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1386,8 +1330,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1447,7 +1392,7 @@ private module Stage2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -1671,8 +1616,6 @@ private module Stage3 {
|
|||||||
|
|
||||||
Cc ccNone() { result = false }
|
Cc ccNone() { result = false }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result = true }
|
|
||||||
|
|
||||||
private class LocalCc = Unit;
|
private class LocalCc = Unit;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -1727,14 +1670,12 @@ private module Stage3 {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1756,7 +1697,7 @@ private module Stage3 {
|
|||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -1859,8 +1800,9 @@ private module Stage3 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1875,8 +1817,9 @@ private module Stage3 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1886,8 +1829,10 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1943,11 +1888,6 @@ private module Stage3 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -1968,7 +1908,7 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -2024,7 +1964,7 @@ private module Stage3 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -2059,8 +1999,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2070,8 +2011,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2081,8 +2023,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2142,7 +2085,7 @@ private module Stage3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -2423,8 +2366,6 @@ private module Stage4 {
|
|||||||
|
|
||||||
Cc ccNone() { result instanceof CallContextAny }
|
Cc ccNone() { result instanceof CallContextAny }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
|
||||||
|
|
||||||
private class LocalCc = LocalCallContext;
|
private class LocalCc = LocalCallContext;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -2493,14 +2434,12 @@ private module Stage4 {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2522,7 +2461,7 @@ private module Stage4 {
|
|||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -2625,8 +2564,9 @@ private module Stage4 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2641,8 +2581,9 @@ private module Stage4 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2652,8 +2593,10 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2709,11 +2652,6 @@ private module Stage4 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -2734,7 +2672,7 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -2790,7 +2728,7 @@ private module Stage4 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -2825,8 +2763,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2836,8 +2775,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2847,8 +2787,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2908,7 +2849,7 @@ private module Stage4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -2992,7 +2933,7 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
|||||||
|
|
||||||
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
|
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
|
||||||
|
|
||||||
ParameterPosition getParameterPos() { p.isParameterOf(_, result) }
|
int getParameterPos() { p.isParameterOf(_, result) }
|
||||||
|
|
||||||
ParamNodeEx getParamNode() { result = p }
|
ParamNodeEx getParamNode() { result = p }
|
||||||
|
|
||||||
@@ -3123,11 +3064,7 @@ private newtype TPathNode =
|
|||||||
// A PathNode is introduced by a source ...
|
// A PathNode is introduced by a source ...
|
||||||
Stage4::revFlow(node, config) and
|
Stage4::revFlow(node, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(
|
cc instanceof CallContextAny and
|
||||||
if hasSourceCallCtx(config)
|
|
||||||
then cc instanceof CallContextSomeCall
|
|
||||||
else cc instanceof CallContextAny
|
|
||||||
) and
|
|
||||||
sc instanceof SummaryCtxNone and
|
sc instanceof SummaryCtxNone and
|
||||||
ap = TAccessPathNil(node.getDataFlowType())
|
ap = TAccessPathNil(node.getDataFlowType())
|
||||||
or
|
or
|
||||||
@@ -3139,10 +3076,17 @@ private newtype TPathNode =
|
|||||||
)
|
)
|
||||||
} or
|
} or
|
||||||
TPathNodeSink(NodeEx node, Configuration config) {
|
TPathNodeSink(NodeEx node, Configuration config) {
|
||||||
exists(PathNodeMid sink |
|
sinkNode(node, pragma[only_bind_into](config)) and
|
||||||
sink.isAtSink() and
|
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||||
node = sink.getNodeEx() and
|
(
|
||||||
config = sink.getConfiguration()
|
// A sink that is also a source ...
|
||||||
|
sourceNode(node, config)
|
||||||
|
or
|
||||||
|
// ... or a sink that can be reached from a source
|
||||||
|
exists(PathNodeMid mid |
|
||||||
|
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||||
|
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3459,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
|||||||
// an intermediate step to another intermediate node
|
// an intermediate step to another intermediate node
|
||||||
result = this.getSuccMid()
|
result = this.getSuccMid()
|
||||||
or
|
or
|
||||||
// a final step to a sink
|
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||||
result = this.getSuccMid().projectToSink()
|
exists(PathNodeMid mid, PathNodeSink sink |
|
||||||
|
mid = this.getSuccMid() and
|
||||||
|
mid.getNodeEx() = sink.getNodeEx() and
|
||||||
|
mid.getAp() instanceof AccessPathNil and
|
||||||
|
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||||
|
result = sink
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSource() {
|
override predicate isSource() {
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(
|
cc instanceof CallContextAny and
|
||||||
if hasSourceCallCtx(config)
|
|
||||||
then cc instanceof CallContextSomeCall
|
|
||||||
else cc instanceof CallContextAny
|
|
||||||
) and
|
|
||||||
sc instanceof SummaryCtxNone and
|
sc instanceof SummaryCtxNone and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate isAtSink() {
|
|
||||||
sinkNode(node, config) and
|
|
||||||
ap instanceof AccessPathNil and
|
|
||||||
if hasSinkCallCtx(config)
|
|
||||||
then
|
|
||||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
|
||||||
// is exactly what we need to check. This also implies
|
|
||||||
// `sc instanceof SummaryCtxNone`.
|
|
||||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
|
||||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
|
||||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
|
||||||
// in this case there's never any need to enter a call except to identify
|
|
||||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
|
||||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
|
||||||
// in the call context of the source.
|
|
||||||
sc instanceof SummaryCtxNone or
|
|
||||||
cc instanceof CallContextNoCall
|
|
||||||
else any()
|
|
||||||
}
|
|
||||||
|
|
||||||
PathNodeSink projectToSink() {
|
|
||||||
this.isAtSink() and
|
|
||||||
result.getNodeEx() = node and
|
|
||||||
result.getConfiguration() = unbindConf(config)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3639,40 +3559,39 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
|||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate pathIntoArg(
|
private predicate pathIntoArg(
|
||||||
PathNodeMid mid, ParameterPosition ppos, CallContext cc, DataFlowCall call, AccessPath ap,
|
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||||
AccessPathApprox apa, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ArgNode arg, ArgumentPosition apos |
|
exists(ArgNode arg |
|
||||||
arg = mid.getNodeEx().asNode() and
|
arg = mid.getNodeEx().asNode() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
arg.argumentOf(call, apos) and
|
arg.argumentOf(call, i) and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
apa = ap.getApprox() and
|
apa = ap.getApprox() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[noinline]
|
||||||
private predicate parameterCand(
|
private predicate parameterCand(
|
||||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p |
|
exists(ParamNodeEx p |
|
||||||
Stage4::revFlow(p, _, _, apa, config) and
|
Stage4::revFlow(p, _, _, apa, config) and
|
||||||
p.isParameterOf(callable, pos)
|
p.isParameterOf(callable, i)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate pathIntoCallable0(
|
private predicate pathIntoCallable0(
|
||||||
PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, CallContext outercc,
|
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||||
DataFlowCall call, AccessPath ap, Configuration config
|
AccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(AccessPathApprox apa |
|
exists(AccessPathApprox apa |
|
||||||
pathIntoArg(mid, pragma[only_bind_into](pos), outercc, call, ap, pragma[only_bind_into](apa),
|
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config)) and
|
||||||
callable = resolveCall(call, outercc) and
|
callable = resolveCall(call, outercc) and
|
||||||
parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa),
|
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||||
pragma[only_bind_into](config))
|
pragma[only_bind_into](config))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3687,18 +3606,14 @@ private predicate pathIntoCallable(
|
|||||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||||
DataFlowCall call, Configuration config
|
DataFlowCall call, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParameterPosition pos, DataFlowCallable callable, AccessPath ap |
|
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||||
pathIntoCallable0(mid, callable, pos, outercc, call, ap, config) and
|
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||||
p.isParameterOf(callable, pos) and
|
p.isParameterOf(callable, i) and
|
||||||
(
|
(
|
||||||
sc = TSummaryCtxSome(p, ap)
|
sc = TSummaryCtxSome(p, ap)
|
||||||
or
|
or
|
||||||
not exists(TSummaryCtxSome(p, ap)) and
|
not exists(TSummaryCtxSome(p, ap)) and
|
||||||
sc = TSummaryCtxNone() and
|
sc = TSummaryCtxNone()
|
||||||
// When the call contexts of source and sink needs to match then there's
|
|
||||||
// never any reason to enter a callable except to find a summary. See also
|
|
||||||
// the comment in `PathNodeMid::isAtSink`.
|
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
)
|
||||||
|
|
|
|
||||||
if recordDataFlowCallSite(call, callable)
|
if recordDataFlowCallSite(call, callable)
|
||||||
@@ -3713,7 +3628,7 @@ private predicate paramFlowsThrough(
|
|||||||
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
|
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
exists(PathNodeMid mid, RetNodeEx ret, int pos |
|
||||||
mid.getNodeEx() = ret and
|
mid.getNodeEx() = ret and
|
||||||
kind = ret.getKind() and
|
kind = ret.getKind() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
@@ -3761,14 +3676,13 @@ private module Subpaths {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate subpaths01(
|
private predicate subpaths01(
|
||||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||||
NodeEx out, AccessPath apout
|
NodeEx out, AccessPath apout
|
||||||
) {
|
) {
|
||||||
exists(Configuration config |
|
exists(Configuration config |
|
||||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) and
|
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||||
not arg.isHidden()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3802,17 +3716,8 @@ private module Subpaths {
|
|||||||
innercc = ret.getCallContext() and
|
innercc = ret.getCallContext() and
|
||||||
sc = ret.getSummaryCtx() and
|
sc = ret.getSummaryCtx() and
|
||||||
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
||||||
apout = ret.getAp()
|
apout = ret.getAp() and
|
||||||
)
|
not ret.isHidden()
|
||||||
}
|
|
||||||
|
|
||||||
private PathNodeImpl localStepToHidden(PathNodeImpl n) {
|
|
||||||
n.getASuccessorImpl() = result and
|
|
||||||
result.isHidden() and
|
|
||||||
exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() |
|
|
||||||
localFlowBigStep(n1, n2, _, _, _, _) or
|
|
||||||
store(n1, _, n2, _, _) or
|
|
||||||
read(n1, _, n2, _)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3821,12 +3726,11 @@ private module Subpaths {
|
|||||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||||
*/
|
*/
|
||||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNodeMid out) {
|
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||||
subpaths03(arg, p, localStepToHidden*(ret), o, apout) and
|
subpaths03(arg, p, ret, o, apout) and
|
||||||
not ret.isHidden() and
|
|
||||||
par.getNodeEx() = p and
|
par.getNodeEx() = p and
|
||||||
out.getNodeEx() = o and
|
out.getNodeEx() = o and
|
||||||
out.getAp() = apout
|
out.getAp() = apout
|
||||||
@@ -4442,25 +4346,24 @@ private module FlowExploration {
|
|||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate partialPathIntoArg(
|
private predicate partialPathIntoArg(
|
||||||
PartialPathNodeFwd mid, ParameterPosition ppos, CallContext cc, DataFlowCall call,
|
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||||
PartialAccessPath ap, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ArgNode arg, ArgumentPosition apos |
|
exists(ArgNode arg |
|
||||||
arg = mid.getNodeEx().asNode() and
|
arg = mid.getNodeEx().asNode() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
arg.argumentOf(call, apos) and
|
arg.argumentOf(call, i) and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate partialPathIntoCallable0(
|
private predicate partialPathIntoCallable0(
|
||||||
PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, CallContext outercc,
|
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
partialPathIntoArg(mid, pos, outercc, call, ap, config) and
|
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||||
callable = resolveCall(call, outercc)
|
callable = resolveCall(call, outercc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4469,9 +4372,9 @@ private module FlowExploration {
|
|||||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParameterPosition pos, DataFlowCallable callable |
|
exists(int i, DataFlowCallable callable |
|
||||||
partialPathIntoCallable0(mid, callable, pos, outercc, call, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||||
p.isParameterOf(callable, pos) and
|
p.isParameterOf(callable, i) and
|
||||||
sc1 = TSummaryCtx1Param(p) and
|
sc1 = TSummaryCtx1Param(p) and
|
||||||
sc2 = TSummaryCtx2Some(ap)
|
sc2 = TSummaryCtx2Some(ap)
|
||||||
|
|
|
|
||||||
@@ -4635,23 +4538,22 @@ private module FlowExploration {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate revPartialPathFlowsThrough(
|
private predicate revPartialPathFlowsThrough(
|
||||||
ArgumentPosition apos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||||
RevPartialAccessPath ap, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos |
|
exists(PartialPathNodeRev mid, ParamNodeEx p |
|
||||||
mid.getNodeEx() = p and
|
mid.getNodeEx() = p and
|
||||||
p.getPosition() = ppos and
|
p.getPosition() = pos and
|
||||||
sc1 = mid.getSummaryCtx1() and
|
sc1 = mid.getSummaryCtx1() and
|
||||||
sc2 = mid.getSummaryCtx2() and
|
sc2 = mid.getSummaryCtx2() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate revPartialPathThroughCallable0(
|
private predicate revPartialPathThroughCallable0(
|
||||||
DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, RevPartialAccessPath ap,
|
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||||
@@ -4664,7 +4566,7 @@ private module FlowExploration {
|
|||||||
private predicate revPartialPathThroughCallable(
|
private predicate revPartialPathThroughCallable(
|
||||||
PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
|
PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(DataFlowCall call, ArgumentPosition pos |
|
exists(DataFlowCall call, int pos |
|
||||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||||
node.asNode().(ArgNode).argumentOf(call, pos)
|
node.asNode().(ArgNode).argumentOf(call, pos)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,42 +2,6 @@ private import DataFlowImplSpecific::Private
|
|||||||
private import DataFlowImplSpecific::Public
|
private import DataFlowImplSpecific::Public
|
||||||
import Cached
|
import Cached
|
||||||
|
|
||||||
module DataFlowImplCommonPublic {
|
|
||||||
private newtype TFlowFeature =
|
|
||||||
TFeatureHasSourceCallContext() or
|
|
||||||
TFeatureHasSinkCallContext() or
|
|
||||||
TFeatureEqualSourceSinkCallContext()
|
|
||||||
|
|
||||||
/** A flow configuration feature for use in `Configuration::getAFeature()`. */
|
|
||||||
class FlowFeature extends TFlowFeature {
|
|
||||||
string toString() { none() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A flow configuration feature that implies that sources have some existing
|
|
||||||
* call context.
|
|
||||||
*/
|
|
||||||
class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext {
|
|
||||||
override string toString() { result = "FeatureHasSourceCallContext" }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A flow configuration feature that implies that sinks have some existing
|
|
||||||
* call context.
|
|
||||||
*/
|
|
||||||
class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext {
|
|
||||||
override string toString() { result = "FeatureHasSinkCallContext" }
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A flow configuration feature that implies that source-sink pairs have some
|
|
||||||
* shared existing call context.
|
|
||||||
*/
|
|
||||||
class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext {
|
|
||||||
override string toString() { result = "FeatureEqualSourceSinkCallContext" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
|
* The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion.
|
||||||
*
|
*
|
||||||
@@ -62,18 +26,6 @@ predicate accessPathCostLimits(int apLimit, int tupleLimit) {
|
|||||||
tupleLimit = 1000
|
tupleLimit = 1000
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `arg` is an argument of `call` with an argument position that matches
|
|
||||||
* parameter position `ppos`.
|
|
||||||
*/
|
|
||||||
pragma[noinline]
|
|
||||||
predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) {
|
|
||||||
exists(ArgumentPosition apos |
|
|
||||||
arg.argumentOf(call, apos) and
|
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a simple data-flow analysis for resolving lambda calls. The analysis
|
* Provides a simple data-flow analysis for resolving lambda calls. The analysis
|
||||||
* currently excludes read-steps, store-steps, and flow-through.
|
* currently excludes read-steps, store-steps, and flow-through.
|
||||||
@@ -83,27 +35,25 @@ predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPositio
|
|||||||
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
|
* calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly.
|
||||||
*/
|
*/
|
||||||
private module LambdaFlow {
|
private module LambdaFlow {
|
||||||
pragma[noinline]
|
private predicate viableParamNonLambda(DataFlowCall call, int i, ParamNode p) {
|
||||||
private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) {
|
p.isParameterOf(viableCallable(call), i)
|
||||||
p.isParameterOf(viableCallable(call), ppos)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[noinline]
|
private predicate viableParamLambda(DataFlowCall call, int i, ParamNode p) {
|
||||||
private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) {
|
p.isParameterOf(viableCallableLambda(call, _), i)
|
||||||
p.isParameterOf(viableCallableLambda(call, _), ppos)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
|
private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
|
||||||
exists(ParameterPosition ppos |
|
exists(int i |
|
||||||
viableParamNonLambda(call, ppos, p) and
|
viableParamNonLambda(call, i, p) and
|
||||||
argumentPositionMatch(call, arg, ppos)
|
arg.argumentOf(call, i)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
|
private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) {
|
||||||
exists(ParameterPosition ppos |
|
exists(int i |
|
||||||
viableParamLambda(call, ppos, p) and
|
viableParamLambda(call, i, p) and
|
||||||
argumentPositionMatch(call, arg, ppos)
|
arg.argumentOf(call, i)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +251,7 @@ private module Cached {
|
|||||||
predicate forceCachingInSameStage() { any() }
|
predicate forceCachingInSameStage() { any() }
|
||||||
|
|
||||||
cached
|
cached
|
||||||
predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) }
|
predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = n.getEnclosingCallable() }
|
||||||
|
|
||||||
cached
|
cached
|
||||||
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
|
predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) {
|
||||||
@@ -336,7 +286,7 @@ private module Cached {
|
|||||||
or
|
or
|
||||||
exists(ArgNode arg |
|
exists(ArgNode arg |
|
||||||
result.(PostUpdateNode).getPreUpdateNode() = arg and
|
result.(PostUpdateNode).getPreUpdateNode() = arg and
|
||||||
arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition())
|
arg.argumentOf(call, k.(ParamUpdateReturnKind).getPosition())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +294,7 @@ private module Cached {
|
|||||||
predicate returnNodeExt(Node n, ReturnKindExt k) {
|
predicate returnNodeExt(Node n, ReturnKindExt k) {
|
||||||
k = TValueReturn(n.(ReturnNode).getKind())
|
k = TValueReturn(n.(ReturnNode).getKind())
|
||||||
or
|
or
|
||||||
exists(ParamNode p, ParameterPosition pos |
|
exists(ParamNode p, int pos |
|
||||||
parameterValueFlowsToPreUpdate(p, n) and
|
parameterValueFlowsToPreUpdate(p, n) and
|
||||||
p.isParameterOf(_, pos) and
|
p.isParameterOf(_, pos) and
|
||||||
k = TParamUpdate(pos)
|
k = TParamUpdate(pos)
|
||||||
@@ -366,13 +316,13 @@ private module Cached {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) {
|
predicate parameterNode(Node n, DataFlowCallable c, int i) {
|
||||||
isParameterNode(p, c, pos)
|
n.(ParameterNode).isParameterOf(c, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
cached
|
cached
|
||||||
predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) {
|
predicate argumentNode(Node n, DataFlowCall call, int pos) {
|
||||||
isArgumentNode(n, call, pos)
|
n.(ArgumentNode).argumentOf(call, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -390,12 +340,12 @@ private module Cached {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `p` is the parameter of a viable dispatch target of `call`,
|
* Holds if `p` is the `i`th parameter of a viable dispatch target of `call`.
|
||||||
* and `p` has position `ppos`.
|
* The instance parameter is considered to have index `-1`.
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) {
|
private predicate viableParam(DataFlowCall call, int i, ParamNode p) {
|
||||||
p.isParameterOf(viableCallableExt(call), ppos)
|
p.isParameterOf(viableCallableExt(call), i)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -404,9 +354,9 @@ private module Cached {
|
|||||||
*/
|
*/
|
||||||
cached
|
cached
|
||||||
predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) {
|
predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) {
|
||||||
exists(ParameterPosition ppos |
|
exists(int i |
|
||||||
viableParam(call, ppos, p) and
|
viableParam(call, i, p) and
|
||||||
argumentPositionMatch(call, arg, ppos) and
|
arg.argumentOf(call, i) and
|
||||||
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p))
|
compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -878,7 +828,7 @@ private module Cached {
|
|||||||
cached
|
cached
|
||||||
newtype TReturnKindExt =
|
newtype TReturnKindExt =
|
||||||
TValueReturn(ReturnKind kind) or
|
TValueReturn(ReturnKind kind) or
|
||||||
TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) }
|
TParamUpdate(int pos) { exists(ParamNode p | p.isParameterOf(_, pos)) }
|
||||||
|
|
||||||
cached
|
cached
|
||||||
newtype TBooleanOption =
|
newtype TBooleanOption =
|
||||||
@@ -1070,9 +1020,9 @@ class ParamNode extends Node {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if this node is the parameter of callable `c` at the specified
|
* Holds if this node is the parameter of callable `c` at the specified
|
||||||
* position.
|
* (zero-based) position.
|
||||||
*/
|
*/
|
||||||
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) }
|
predicate isParameterOf(DataFlowCallable c, int i) { parameterNode(this, c, i) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A data-flow node that represents a call argument. */
|
/** A data-flow node that represents a call argument. */
|
||||||
@@ -1080,9 +1030,7 @@ class ArgNode extends Node {
|
|||||||
ArgNode() { argumentNode(this, _, _) }
|
ArgNode() { argumentNode(this, _, _) }
|
||||||
|
|
||||||
/** Holds if this argument occurs at the given position in the given call. */
|
/** Holds if this argument occurs at the given position in the given call. */
|
||||||
final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
|
final predicate argumentOf(DataFlowCall call, int pos) { argumentNode(this, call, pos) }
|
||||||
argumentNode(this, call, pos)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1128,14 +1076,11 @@ class ValueReturnKind extends ReturnKindExt, TValueReturn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
|
class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate {
|
||||||
private ParameterPosition pos;
|
private int pos;
|
||||||
|
|
||||||
ParamUpdateReturnKind() { this = TParamUpdate(pos) }
|
ParamUpdateReturnKind() { this = TParamUpdate(pos) }
|
||||||
|
|
||||||
ParameterPosition getPosition() { result = pos }
|
int getPosition() { result = pos }
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) }
|
|
||||||
|
|
||||||
override string toString() { result = "param update " + pos }
|
override string toString() { result = "param update " + pos }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,31 +9,6 @@ private import tainttracking1.TaintTrackingParameter::Private
|
|||||||
private import tainttracking1.TaintTrackingParameter::Public
|
private import tainttracking1.TaintTrackingParameter::Public
|
||||||
|
|
||||||
module Consistency {
|
module Consistency {
|
||||||
private newtype TConsistencyConfiguration = MkConsistencyConfiguration()
|
|
||||||
|
|
||||||
/** A class for configuring the consistency queries. */
|
|
||||||
class ConsistencyConfiguration extends TConsistencyConfiguration {
|
|
||||||
string toString() { none() }
|
|
||||||
|
|
||||||
/** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */
|
|
||||||
predicate uniqueEnclosingCallableExclude(Node n) { none() }
|
|
||||||
|
|
||||||
/** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */
|
|
||||||
predicate uniqueNodeLocationExclude(Node n) { none() }
|
|
||||||
|
|
||||||
/** Holds if `n` should be excluded from the consistency test `missingLocation`. */
|
|
||||||
predicate missingLocationExclude(Node n) { none() }
|
|
||||||
|
|
||||||
/** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */
|
|
||||||
predicate postWithInFlowExclude(Node n) { none() }
|
|
||||||
|
|
||||||
/** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */
|
|
||||||
predicate argHasPostUpdateExclude(ArgumentNode n) { none() }
|
|
||||||
|
|
||||||
/** Holds if `n` should be excluded from the consistency test `reverseRead`. */
|
|
||||||
predicate reverseReadExclude(Node n) { none() }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class RelevantNode extends Node {
|
private class RelevantNode extends Node {
|
||||||
RelevantNode() {
|
RelevantNode() {
|
||||||
this instanceof ArgumentNode or
|
this instanceof ArgumentNode or
|
||||||
@@ -56,9 +31,8 @@ module Consistency {
|
|||||||
query predicate uniqueEnclosingCallable(Node n, string msg) {
|
query predicate uniqueEnclosingCallable(Node n, string msg) {
|
||||||
exists(int c |
|
exists(int c |
|
||||||
n instanceof RelevantNode and
|
n instanceof RelevantNode and
|
||||||
c = count(nodeGetEnclosingCallable(n)) and
|
c = count(n.getEnclosingCallable()) and
|
||||||
c != 1 and
|
c != 1 and
|
||||||
not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and
|
|
||||||
msg = "Node should have one enclosing callable but has " + c + "."
|
msg = "Node should have one enclosing callable but has " + c + "."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -79,7 +53,6 @@ module Consistency {
|
|||||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||||
) and
|
) and
|
||||||
c != 1 and
|
c != 1 and
|
||||||
not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and
|
|
||||||
msg = "Node should have one location but has " + c + "."
|
msg = "Node should have one location but has " + c + "."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -90,8 +63,7 @@ module Consistency {
|
|||||||
strictcount(Node n |
|
strictcount(Node n |
|
||||||
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn |
|
||||||
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn)
|
||||||
) and
|
)
|
||||||
not any(ConsistencyConfiguration conf).missingLocationExclude(n)
|
|
||||||
) and
|
) and
|
||||||
msg = "Nodes without location: " + c
|
msg = "Nodes without location: " + c
|
||||||
)
|
)
|
||||||
@@ -113,13 +85,13 @@ module Consistency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
query predicate parameterCallable(ParameterNode p, string msg) {
|
query predicate parameterCallable(ParameterNode p, string msg) {
|
||||||
exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and
|
exists(DataFlowCallable c | p.isParameterOf(c, _) and c != p.getEnclosingCallable()) and
|
||||||
msg = "Callable mismatch for parameter."
|
msg = "Callable mismatch for parameter."
|
||||||
}
|
}
|
||||||
|
|
||||||
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
|
query predicate localFlowIsLocal(Node n1, Node n2, string msg) {
|
||||||
simpleLocalFlowStep(n1, n2) and
|
simpleLocalFlowStep(n1, n2) and
|
||||||
nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and
|
n1.getEnclosingCallable() != n2.getEnclosingCallable() and
|
||||||
msg = "Local flow step does not preserve enclosing callable."
|
msg = "Local flow step does not preserve enclosing callable."
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +106,7 @@ module Consistency {
|
|||||||
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
|
query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) {
|
||||||
isUnreachableInCall(n, call) and
|
isUnreachableInCall(n, call) and
|
||||||
exists(DataFlowCallable c |
|
exists(DataFlowCallable c |
|
||||||
c = nodeGetEnclosingCallable(n) and
|
c = n.getEnclosingCallable() and
|
||||||
not viableCallable(call) = c
|
not viableCallable(call) = c
|
||||||
) and
|
) and
|
||||||
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
|
msg = "Call context for isUnreachableInCall is inconsistent with call graph."
|
||||||
@@ -148,7 +120,7 @@ module Consistency {
|
|||||||
n.(ArgumentNode).argumentOf(call, _) and
|
n.(ArgumentNode).argumentOf(call, _) and
|
||||||
msg = "ArgumentNode and call does not share enclosing callable."
|
msg = "ArgumentNode and call does not share enclosing callable."
|
||||||
) and
|
) and
|
||||||
nodeGetEnclosingCallable(n) != call.getEnclosingCallable()
|
n.getEnclosingCallable() != call.getEnclosingCallable()
|
||||||
}
|
}
|
||||||
|
|
||||||
// This predicate helps the compiler forget that in some languages
|
// This predicate helps the compiler forget that in some languages
|
||||||
@@ -179,7 +151,7 @@ module Consistency {
|
|||||||
}
|
}
|
||||||
|
|
||||||
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
|
query predicate postIsInSameCallable(PostUpdateNode n, string msg) {
|
||||||
nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and
|
n.getEnclosingCallable() != n.getPreUpdateNode().getEnclosingCallable() and
|
||||||
msg = "PostUpdateNode does not share callable with its pre-update node."
|
msg = "PostUpdateNode does not share callable with its pre-update node."
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,13 +159,12 @@ module Consistency {
|
|||||||
|
|
||||||
query predicate reverseRead(Node n, string msg) {
|
query predicate reverseRead(Node n, string msg) {
|
||||||
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
|
exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and
|
||||||
not any(ConsistencyConfiguration conf).reverseReadExclude(n) and
|
|
||||||
msg = "Origin of readStep is missing a PostUpdateNode."
|
msg = "Origin of readStep is missing a PostUpdateNode."
|
||||||
}
|
}
|
||||||
|
|
||||||
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
|
query predicate argHasPostUpdate(ArgumentNode n, string msg) {
|
||||||
not hasPost(n) and
|
not hasPost(n) and
|
||||||
not any(ConsistencyConfiguration c).argHasPostUpdateExclude(n) and
|
not isImmutableOrUnobservable(n) and
|
||||||
msg = "ArgumentNode is missing PostUpdateNode."
|
msg = "ArgumentNode is missing PostUpdateNode."
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +177,6 @@ module Consistency {
|
|||||||
isPostUpdateNode(n) and
|
isPostUpdateNode(n) and
|
||||||
not clearsContent(n, _) and
|
not clearsContent(n, _) and
|
||||||
simpleLocalFlowStep(_, n) and
|
simpleLocalFlowStep(_, n) and
|
||||||
not any(ConsistencyConfiguration c).postWithInFlowExclude(n) and
|
|
||||||
msg = "PostUpdateNode should not be the target of local flow."
|
msg = "PostUpdateNode should not be the target of local flow."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
private import DataFlowImplCommon
|
private import DataFlowImplCommon
|
||||||
private import DataFlowImplSpecific::Private
|
private import DataFlowImplSpecific::Private
|
||||||
import DataFlowImplSpecific::Public
|
import DataFlowImplSpecific::Public
|
||||||
import DataFlowImplCommonPublic
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A configuration of interprocedural data flow analysis. This defines
|
* A configuration of interprocedural data flow analysis. This defines
|
||||||
@@ -95,22 +94,6 @@ abstract class Configuration extends string {
|
|||||||
*/
|
*/
|
||||||
int fieldFlowBranchLimit() { result = 2 }
|
int fieldFlowBranchLimit() { result = 2 }
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a data flow configuration feature to add restrictions to the set of
|
|
||||||
* valid flow paths.
|
|
||||||
*
|
|
||||||
* - `FeatureHasSourceCallContext`:
|
|
||||||
* Assume that sources have some existing call context to disallow
|
|
||||||
* conflicting return-flow directly following the source.
|
|
||||||
* - `FeatureHasSinkCallContext`:
|
|
||||||
* Assume that sinks have some existing call context to disallow
|
|
||||||
* conflicting argument-to-parameter flow directly preceding the sink.
|
|
||||||
* - `FeatureEqualSourceSinkCallContext`:
|
|
||||||
* Implies both of the above and additionally ensures that the entire flow
|
|
||||||
* path preserves the call context.
|
|
||||||
*/
|
|
||||||
FlowFeature getAFeature() { none() }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data may flow from `source` to `sink` for this configuration.
|
* Holds if data may flow from `source` to `sink` for this configuration.
|
||||||
*/
|
*/
|
||||||
@@ -256,11 +239,11 @@ private class ArgNodeEx extends NodeEx {
|
|||||||
private class ParamNodeEx extends NodeEx {
|
private class ParamNodeEx extends NodeEx {
|
||||||
ParamNodeEx() { this.asNode() instanceof ParamNode }
|
ParamNodeEx() { this.asNode() instanceof ParamNode }
|
||||||
|
|
||||||
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
|
predicate isParameterOf(DataFlowCallable c, int i) {
|
||||||
this.asNode().(ParamNode).isParameterOf(c, pos)
|
this.asNode().(ParamNode).isParameterOf(c, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
ParameterPosition getPosition() { this.isParameterOf(_, result) }
|
int getPosition() { this.isParameterOf(_, result) }
|
||||||
|
|
||||||
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) }
|
||||||
}
|
}
|
||||||
@@ -289,7 +272,6 @@ private predicate outBarrier(NodeEx node, Configuration config) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate fullBarrier(NodeEx node, Configuration config) {
|
private predicate fullBarrier(NodeEx node, Configuration config) {
|
||||||
exists(Node n | node.asNode() = n |
|
exists(Node n | node.asNode() = n |
|
||||||
config.isBarrier(n)
|
config.isBarrier(n)
|
||||||
@@ -308,23 +290,11 @@ private predicate fullBarrier(NodeEx node, Configuration config) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate sourceNode(NodeEx node, Configuration config) {
|
private predicate sourceNode(NodeEx node, Configuration config) { config.isSource(node.asNode()) }
|
||||||
config.isSource(node.asNode()) and
|
|
||||||
not fullBarrier(node, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
|
private predicate sinkNode(NodeEx node, Configuration config) { config.isSink(node.asNode()) }
|
||||||
|
|
||||||
/** Provides the relevant barriers for a step from `node1` to `node2`. */
|
|
||||||
pragma[inline]
|
|
||||||
private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) {
|
|
||||||
not outBarrier(node1, config) and
|
|
||||||
not inBarrier(node2, config) and
|
|
||||||
not fullBarrier(node1, config) and
|
|
||||||
not fullBarrier(node2, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if data can flow in one local step from `node1` to `node2`.
|
* Holds if data can flow in one local step from `node1` to `node2`.
|
||||||
*/
|
*/
|
||||||
@@ -333,14 +303,16 @@ private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config
|
|||||||
node1.asNode() = n1 and
|
node1.asNode() = n1 and
|
||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
simpleLocalFlowStepExt(n1, n2) and
|
simpleLocalFlowStepExt(n1, n2) and
|
||||||
stepFilter(node1, node2, config)
|
not outBarrier(node1, config) and
|
||||||
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
config.allowImplicitRead(n, _) and
|
config.allowImplicitRead(n, _) and
|
||||||
node1.asNode() = n and
|
node1.asNode() = n and
|
||||||
node2.isImplicitReadNode(n, false) and
|
node2.isImplicitReadNode(n, false)
|
||||||
not fullBarrier(node1, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -353,14 +325,16 @@ private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configurat
|
|||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
config.isAdditionalFlowStep(n1, n2) and
|
config.isAdditionalFlowStep(n1, n2) and
|
||||||
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
|
getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and
|
||||||
stepFilter(node1, node2, config)
|
not outBarrier(node1, config) and
|
||||||
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
config.allowImplicitRead(n, _) and
|
config.allowImplicitRead(n, _) and
|
||||||
node1.isImplicitReadNode(n, true) and
|
node1.isImplicitReadNode(n, true) and
|
||||||
node2.asNode() = n and
|
node2.asNode() = n
|
||||||
not fullBarrier(node2, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -372,8 +346,10 @@ private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) {
|
|||||||
node1.asNode() = n1 and
|
node1.asNode() = n1 and
|
||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
jumpStepCached(n1, n2) and
|
jumpStepCached(n1, n2) and
|
||||||
stepFilter(node1, node2, config) and
|
not outBarrier(node1, config) and
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,14 +362,15 @@ private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration c
|
|||||||
node2.asNode() = n2 and
|
node2.asNode() = n2 and
|
||||||
config.isAdditionalFlowStep(n1, n2) and
|
config.isAdditionalFlowStep(n1, n2) and
|
||||||
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
|
getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and
|
||||||
stepFilter(node1, node2, config) and
|
not outBarrier(node1, config) and
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
not inBarrier(node2, config) and
|
||||||
|
not fullBarrier(node1, config) and
|
||||||
|
not fullBarrier(node2, config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) {
|
||||||
read(node1.asNode(), c, node2.asNode()) and
|
read(node1.asNode(), c, node2.asNode())
|
||||||
stepFilter(node1, node2, config)
|
|
||||||
or
|
or
|
||||||
exists(Node n |
|
exists(Node n |
|
||||||
node2.isImplicitReadNode(n, true) and
|
node2.isImplicitReadNode(n, true) and
|
||||||
@@ -406,8 +383,7 @@ private predicate store(
|
|||||||
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config
|
||||||
) {
|
) {
|
||||||
store(node1.asNode(), tc, node2.asNode(), contentType) and
|
store(node1.asNode(), tc, node2.asNode(), contentType) and
|
||||||
read(_, tc.getContent(), _, config) and
|
read(_, tc.getContent(), _, config)
|
||||||
stepFilter(node1, node2, config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
@@ -425,20 +401,6 @@ private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx a
|
|||||||
*/
|
*/
|
||||||
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 }
|
||||||
|
|
||||||
private predicate hasSourceCallCtx(Configuration config) {
|
|
||||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
|
||||||
feature instanceof FeatureHasSourceCallContext or
|
|
||||||
feature instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private predicate hasSinkCallCtx(Configuration config) {
|
|
||||||
exists(FlowFeature feature | feature = config.getAFeature() |
|
|
||||||
feature instanceof FeatureHasSinkCallContext or
|
|
||||||
feature instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private module Stage1 {
|
private module Stage1 {
|
||||||
class ApApprox = Unit;
|
class ApApprox = Unit;
|
||||||
|
|
||||||
@@ -456,8 +418,10 @@ private module Stage1 {
|
|||||||
* argument in a call.
|
* argument in a call.
|
||||||
*/
|
*/
|
||||||
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
predicate fwdFlow(NodeEx node, Cc cc, Configuration config) {
|
||||||
|
not fullBarrier(node, config) and
|
||||||
|
(
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
if hasSourceCallCtx(config) then cc = true else cc = false
|
cc = false
|
||||||
or
|
or
|
||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
fwdFlow(mid, cc, config) and
|
fwdFlow(mid, cc, config) and
|
||||||
@@ -485,21 +449,22 @@ private module Stage1 {
|
|||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
useFieldFlow(config) and
|
useFieldFlow(config) and
|
||||||
fwdFlow(mid, cc, config) and
|
fwdFlow(mid, cc, config) and
|
||||||
store(mid, _, node, _, config)
|
store(mid, _, node, _, config) and
|
||||||
|
not outBarrier(mid, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// read
|
// read
|
||||||
exists(Content c |
|
exists(Content c |
|
||||||
fwdFlowRead(c, node, cc, config) and
|
fwdFlowRead(c, node, cc, config) and
|
||||||
fwdFlowConsCand(c, config)
|
fwdFlowConsCand(c, config) and
|
||||||
|
not inBarrier(node, config)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow into a callable
|
// flow into a callable
|
||||||
exists(NodeEx arg |
|
exists(NodeEx arg |
|
||||||
fwdFlow(arg, _, config) and
|
fwdFlow(arg, _, config) and
|
||||||
viableParamArgEx(_, node, arg) and
|
viableParamArgEx(_, node, arg) and
|
||||||
cc = true and
|
cc = true
|
||||||
not fullBarrier(node, config)
|
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
@@ -510,6 +475,7 @@ private module Stage1 {
|
|||||||
fwdFlowOutFromArg(call, node, config) and
|
fwdFlowOutFromArg(call, node, config) and
|
||||||
fwdFlowIsEntered(call, cc, config)
|
fwdFlowIsEntered(call, cc, config)
|
||||||
)
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) }
|
||||||
@@ -548,8 +514,7 @@ private module Stage1 {
|
|||||||
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) {
|
||||||
exists(ReturnPosition pos |
|
exists(ReturnPosition pos |
|
||||||
fwdFlowReturnPosition(pos, cc, config) and
|
fwdFlowReturnPosition(pos, cc, config) and
|
||||||
viableReturnPosOutEx(call, pos, out) and
|
viableReturnPosOutEx(call, pos, out)
|
||||||
not fullBarrier(out, config)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +551,7 @@ private module Stage1 {
|
|||||||
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) {
|
||||||
fwdFlow(node, config) and
|
fwdFlow(node, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
if hasSinkCallCtx(config) then toReturn = true else toReturn = false
|
toReturn = false
|
||||||
or
|
or
|
||||||
exists(NodeEx mid |
|
exists(NodeEx mid |
|
||||||
localFlowStep(node, mid, config) and
|
localFlowStep(node, mid, config) and
|
||||||
@@ -775,7 +740,6 @@ private module Stage1 {
|
|||||||
* Holds if flow may enter through `p` and reach a return node making `p` a
|
* Holds if flow may enter through `p` and reach a return node making `p` a
|
||||||
* candidate for the origin of a summary.
|
* candidate for the origin of a summary.
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(ReturnKindExt kind |
|
exists(ReturnKindExt kind |
|
||||||
throughFlowNodeCand(p, config) and
|
throughFlowNodeCand(p, config) and
|
||||||
@@ -973,8 +937,6 @@ private module Stage2 {
|
|||||||
|
|
||||||
Cc ccNone() { result instanceof CallContextAny }
|
Cc ccNone() { result instanceof CallContextAny }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
|
||||||
|
|
||||||
private class LocalCc = Unit;
|
private class LocalCc = Unit;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -1012,9 +974,6 @@ private module Stage2 {
|
|||||||
|
|
||||||
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
private predicate flowIntoCall = flowIntoCallNodeCand1/5;
|
||||||
|
|
||||||
bindingset[node, ap]
|
|
||||||
private predicate filter(NodeEx node, Ap ap) { any() }
|
|
||||||
|
|
||||||
bindingset[ap, contentType]
|
bindingset[ap, contentType]
|
||||||
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
private predicate typecheckStore(Ap ap, DataFlowType contentType) { any() }
|
||||||
|
|
||||||
@@ -1023,23 +982,14 @@ private module Stage2 {
|
|||||||
PrevStage::revFlow(node, _, _, apa, config)
|
PrevStage::revFlow(node, _, _, apa, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingset[result, apa]
|
|
||||||
private ApApprox unbindApa(ApApprox apa) {
|
|
||||||
exists(ApApprox apa0 |
|
|
||||||
apa = pragma[only_bind_into](apa0) and result = pragma[only_bind_into](apa0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1052,16 +1002,9 @@ private module Stage2 {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
predicate fwdFlow(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
fwdFlow0(node, cc, argAp, ap, config) and
|
|
||||||
flowCand(node, unbindApa(getApprox(ap)), config) and
|
|
||||||
filter(node, ap)
|
|
||||||
}
|
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -1129,7 +1072,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(DataFlowType contentType |
|
exists(DataFlowType contentType |
|
||||||
fwdFlow(node1, cc, argAp, ap1, config) and
|
fwdFlow(node1, cc, argAp, ap1, config) and
|
||||||
PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and
|
PrevStage::storeStepCand(node1, getApprox(ap1), tc, node2, contentType, config) and
|
||||||
typecheckStore(ap1, contentType)
|
typecheckStore(ap1, contentType)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1164,8 +1107,9 @@ private module Stage2 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1180,8 +1124,9 @@ private module Stage2 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,8 +1136,10 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1206,7 +1153,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p |
|
exists(ParamNodeEx p |
|
||||||
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
|
fwdFlowIn(call, p, cc, _, argAp, ap, config) and
|
||||||
PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config)
|
PrevStage::parameterMayFlowThrough(p, _, getApprox(ap), config)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1248,11 +1195,6 @@ private module Stage2 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -1273,7 +1215,7 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -1329,7 +1271,7 @@ private module Stage2 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -1364,8 +1306,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,8 +1318,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1386,8 +1330,9 @@ private module Stage2 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1447,7 +1392,7 @@ private module Stage2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -1671,8 +1616,6 @@ private module Stage3 {
|
|||||||
|
|
||||||
Cc ccNone() { result = false }
|
Cc ccNone() { result = false }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result = true }
|
|
||||||
|
|
||||||
private class LocalCc = Unit;
|
private class LocalCc = Unit;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -1727,14 +1670,12 @@ private module Stage3 {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1756,7 +1697,7 @@ private module Stage3 {
|
|||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -1859,8 +1800,9 @@ private module Stage3 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1875,8 +1817,9 @@ private module Stage3 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1886,8 +1829,10 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1943,11 +1888,6 @@ private module Stage3 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -1968,7 +1908,7 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -2024,7 +1964,7 @@ private module Stage3 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -2059,8 +1999,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2070,8 +2011,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2081,8 +2023,9 @@ private module Stage3 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2142,7 +2085,7 @@ private module Stage3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -2423,8 +2366,6 @@ private module Stage4 {
|
|||||||
|
|
||||||
Cc ccNone() { result instanceof CallContextAny }
|
Cc ccNone() { result instanceof CallContextAny }
|
||||||
|
|
||||||
CcCall ccSomeCall() { result instanceof CallContextSomeCall }
|
|
||||||
|
|
||||||
private class LocalCc = LocalCallContext;
|
private class LocalCc = LocalCallContext;
|
||||||
|
|
||||||
bindingset[call, c, outercc]
|
bindingset[call, c, outercc]
|
||||||
@@ -2493,14 +2434,12 @@ private module Stage4 {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate flowThroughOutOfCall(
|
private predicate flowThroughOutOfCall(
|
||||||
DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow,
|
DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config
|
||||||
Configuration config
|
|
||||||
) {
|
) {
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and
|
||||||
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and
|
||||||
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _,
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config))
|
||||||
ccc.matchesCall(call)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2522,7 +2461,7 @@ private module Stage4 {
|
|||||||
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
private predicate fwdFlow0(NodeEx node, Cc cc, ApOption argAp, Ap ap, Configuration config) {
|
||||||
flowCand(node, _, config) and
|
flowCand(node, _, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and
|
cc = ccNone() and
|
||||||
argAp = apNone() and
|
argAp = apNone() and
|
||||||
ap = getApNil(node)
|
ap = getApNil(node)
|
||||||
or
|
or
|
||||||
@@ -2625,8 +2564,9 @@ private module Stage4 {
|
|||||||
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
exists(ArgNodeEx arg, boolean allowsFieldFlow |
|
||||||
fwdFlow(arg, outercc, argAp, ap, config) and
|
fwdFlow(arg, outercc, argAp, ap, config) and
|
||||||
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(call, arg, p, allowsFieldFlow, config) and
|
||||||
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and
|
innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2641,8 +2581,9 @@ private module Stage4 {
|
|||||||
fwdFlow(ret, innercc, argAp, ap, config) and
|
fwdFlow(ret, innercc, argAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
inner = ret.getEnclosingCallable() and
|
inner = ret.getEnclosingCallable() and
|
||||||
ccOut = getCallContextReturn(inner, call, innercc) and
|
ccOut = getCallContextReturn(inner, call, innercc)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2652,8 +2593,10 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc |
|
||||||
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
fwdFlow(ret, ccc, apSome(argAp), ap, config) and
|
||||||
flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and
|
flowThroughOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
ccc.matchesCall(call)
|
||||||
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2709,11 +2652,6 @@ private module Stage4 {
|
|||||||
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
callMayFlowThroughFwd(call, pragma[only_bind_into](config))
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
|
||||||
private predicate returnNodeMayFlowThrough(RetNodeEx ret, Ap ap, Configuration config) {
|
|
||||||
fwdFlow(ret, any(CcCall ccc), apSome(_), ap, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `node` with access path `ap` is part of a path from a source to a
|
* Holds if `node` with access path `ap` is part of a path from a source to a
|
||||||
* sink in the configuration `config`.
|
* sink in the configuration `config`.
|
||||||
@@ -2734,7 +2672,7 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
fwdFlow(node, _, _, ap, config) and
|
fwdFlow(node, _, _, ap, config) and
|
||||||
sinkNode(node, config) and
|
sinkNode(node, config) and
|
||||||
(if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and
|
toReturn = false and
|
||||||
returnAp = apNone() and
|
returnAp = apNone() and
|
||||||
ap instanceof ApNil
|
ap instanceof ApNil
|
||||||
or
|
or
|
||||||
@@ -2790,7 +2728,7 @@ private module Stage4 {
|
|||||||
// flow out of a callable
|
// flow out of a callable
|
||||||
revFlowOut(_, node, _, _, ap, config) and
|
revFlowOut(_, node, _, _, ap, config) and
|
||||||
toReturn = true and
|
toReturn = true and
|
||||||
if returnNodeMayFlowThrough(node, ap, config)
|
if fwdFlow(node, any(CcCall ccc), apSome(_), ap, config)
|
||||||
then returnAp = apSome(ap)
|
then returnAp = apSome(ap)
|
||||||
else returnAp = apNone()
|
else returnAp = apNone()
|
||||||
}
|
}
|
||||||
@@ -2825,8 +2763,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(NodeEx out, boolean allowsFieldFlow |
|
exists(NodeEx out, boolean allowsFieldFlow |
|
||||||
revFlow(out, toReturn, returnAp, ap, config) and
|
revFlow(out, toReturn, returnAp, ap, config) and
|
||||||
flowOutOfCall(call, ret, out, allowsFieldFlow, config) and
|
flowOutOfCall(call, ret, out, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2836,8 +2775,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, false, returnAp, ap, config) and
|
revFlow(p, false, returnAp, ap, config) and
|
||||||
flowIntoCall(_, arg, p, allowsFieldFlow, config) and
|
flowIntoCall(_, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2847,8 +2787,9 @@ private module Stage4 {
|
|||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
exists(ParamNodeEx p, boolean allowsFieldFlow |
|
||||||
revFlow(p, true, apSome(returnAp), ap, config) and
|
revFlow(p, true, apSome(returnAp), ap, config) and
|
||||||
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and
|
flowThroughIntoCall(call, arg, p, allowsFieldFlow, config)
|
||||||
if allowsFieldFlow = false then ap instanceof ApNil else any()
|
|
|
||||||
|
ap instanceof ApNil or allowsFieldFlow = true
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2908,7 +2849,7 @@ private module Stage4 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) {
|
||||||
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, ParameterPosition pos |
|
exists(RetNodeEx ret, Ap ap0, ReturnKindExt kind, int pos |
|
||||||
parameterFlow(p, ap, ap0, c, config) and
|
parameterFlow(p, ap, ap0, c, config) and
|
||||||
c = ret.getEnclosingCallable() and
|
c = ret.getEnclosingCallable() and
|
||||||
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
revFlow(pragma[only_bind_into](ret), true, apSome(_), pragma[only_bind_into](ap0),
|
||||||
@@ -2992,7 +2933,7 @@ private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome {
|
|||||||
|
|
||||||
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
|
SummaryCtxSome() { this = TSummaryCtxSome(p, ap) }
|
||||||
|
|
||||||
ParameterPosition getParameterPos() { p.isParameterOf(_, result) }
|
int getParameterPos() { p.isParameterOf(_, result) }
|
||||||
|
|
||||||
ParamNodeEx getParamNode() { result = p }
|
ParamNodeEx getParamNode() { result = p }
|
||||||
|
|
||||||
@@ -3123,11 +3064,7 @@ private newtype TPathNode =
|
|||||||
// A PathNode is introduced by a source ...
|
// A PathNode is introduced by a source ...
|
||||||
Stage4::revFlow(node, config) and
|
Stage4::revFlow(node, config) and
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(
|
cc instanceof CallContextAny and
|
||||||
if hasSourceCallCtx(config)
|
|
||||||
then cc instanceof CallContextSomeCall
|
|
||||||
else cc instanceof CallContextAny
|
|
||||||
) and
|
|
||||||
sc instanceof SummaryCtxNone and
|
sc instanceof SummaryCtxNone and
|
||||||
ap = TAccessPathNil(node.getDataFlowType())
|
ap = TAccessPathNil(node.getDataFlowType())
|
||||||
or
|
or
|
||||||
@@ -3139,10 +3076,17 @@ private newtype TPathNode =
|
|||||||
)
|
)
|
||||||
} or
|
} or
|
||||||
TPathNodeSink(NodeEx node, Configuration config) {
|
TPathNodeSink(NodeEx node, Configuration config) {
|
||||||
exists(PathNodeMid sink |
|
sinkNode(node, pragma[only_bind_into](config)) and
|
||||||
sink.isAtSink() and
|
Stage4::revFlow(node, pragma[only_bind_into](config)) and
|
||||||
node = sink.getNodeEx() and
|
(
|
||||||
config = sink.getConfiguration()
|
// A sink that is also a source ...
|
||||||
|
sourceNode(node, config)
|
||||||
|
or
|
||||||
|
// ... or a sink that can be reached from a source
|
||||||
|
exists(PathNodeMid mid |
|
||||||
|
pathStep(mid, node, _, _, TAccessPathNil(_)) and
|
||||||
|
pragma[only_bind_into](config) = mid.getConfiguration()
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3459,46 +3403,22 @@ private class PathNodeMid extends PathNodeImpl, TPathNodeMid {
|
|||||||
// an intermediate step to another intermediate node
|
// an intermediate step to another intermediate node
|
||||||
result = this.getSuccMid()
|
result = this.getSuccMid()
|
||||||
or
|
or
|
||||||
// a final step to a sink
|
// a final step to a sink via zero steps means we merge the last two steps to prevent trivial-looking edges
|
||||||
result = this.getSuccMid().projectToSink()
|
exists(PathNodeMid mid, PathNodeSink sink |
|
||||||
|
mid = this.getSuccMid() and
|
||||||
|
mid.getNodeEx() = sink.getNodeEx() and
|
||||||
|
mid.getAp() instanceof AccessPathNil and
|
||||||
|
sink.getConfiguration() = unbindConf(mid.getConfiguration()) and
|
||||||
|
result = sink
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override predicate isSource() {
|
override predicate isSource() {
|
||||||
sourceNode(node, config) and
|
sourceNode(node, config) and
|
||||||
(
|
cc instanceof CallContextAny and
|
||||||
if hasSourceCallCtx(config)
|
|
||||||
then cc instanceof CallContextSomeCall
|
|
||||||
else cc instanceof CallContextAny
|
|
||||||
) and
|
|
||||||
sc instanceof SummaryCtxNone and
|
sc instanceof SummaryCtxNone and
|
||||||
ap instanceof AccessPathNil
|
ap instanceof AccessPathNil
|
||||||
}
|
}
|
||||||
|
|
||||||
predicate isAtSink() {
|
|
||||||
sinkNode(node, config) and
|
|
||||||
ap instanceof AccessPathNil and
|
|
||||||
if hasSinkCallCtx(config)
|
|
||||||
then
|
|
||||||
// For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall`
|
|
||||||
// is exactly what we need to check. This also implies
|
|
||||||
// `sc instanceof SummaryCtxNone`.
|
|
||||||
// For `FeatureEqualSourceSinkCallContext` the initial call context was
|
|
||||||
// set to `CallContextSomeCall` and jumps are disallowed, so
|
|
||||||
// `cc instanceof CallContextNoCall` never holds. On the other hand,
|
|
||||||
// in this case there's never any need to enter a call except to identify
|
|
||||||
// a summary, so the condition in `pathIntoCallable` enforces this, which
|
|
||||||
// means that `sc instanceof SummaryCtxNone` holds if and only if we are
|
|
||||||
// in the call context of the source.
|
|
||||||
sc instanceof SummaryCtxNone or
|
|
||||||
cc instanceof CallContextNoCall
|
|
||||||
else any()
|
|
||||||
}
|
|
||||||
|
|
||||||
PathNodeSink projectToSink() {
|
|
||||||
this.isAtSink() and
|
|
||||||
result.getNodeEx() = node and
|
|
||||||
result.getConfiguration() = unbindConf(config)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -3639,40 +3559,39 @@ private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, CallContext cc)
|
|||||||
*/
|
*/
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate pathIntoArg(
|
private predicate pathIntoArg(
|
||||||
PathNodeMid mid, ParameterPosition ppos, CallContext cc, DataFlowCall call, AccessPath ap,
|
PathNodeMid mid, int i, CallContext cc, DataFlowCall call, AccessPath ap, AccessPathApprox apa,
|
||||||
AccessPathApprox apa, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ArgNode arg, ArgumentPosition apos |
|
exists(ArgNode arg |
|
||||||
arg = mid.getNodeEx().asNode() and
|
arg = mid.getNodeEx().asNode() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
arg.argumentOf(call, apos) and
|
arg.argumentOf(call, i) and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
apa = ap.getApprox() and
|
apa = ap.getApprox() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[noinline]
|
||||||
private predicate parameterCand(
|
private predicate parameterCand(
|
||||||
DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config
|
DataFlowCallable callable, int i, AccessPathApprox apa, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParamNodeEx p |
|
exists(ParamNodeEx p |
|
||||||
Stage4::revFlow(p, _, _, apa, config) and
|
Stage4::revFlow(p, _, _, apa, config) and
|
||||||
p.isParameterOf(callable, pos)
|
p.isParameterOf(callable, i)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate pathIntoCallable0(
|
private predicate pathIntoCallable0(
|
||||||
PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, CallContext outercc,
|
PathNodeMid mid, DataFlowCallable callable, int i, CallContext outercc, DataFlowCall call,
|
||||||
DataFlowCall call, AccessPath ap, Configuration config
|
AccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(AccessPathApprox apa |
|
exists(AccessPathApprox apa |
|
||||||
pathIntoArg(mid, pragma[only_bind_into](pos), outercc, call, ap, pragma[only_bind_into](apa),
|
pathIntoArg(mid, pragma[only_bind_into](i), outercc, call, ap, pragma[only_bind_into](apa),
|
||||||
pragma[only_bind_into](config)) and
|
pragma[only_bind_into](config)) and
|
||||||
callable = resolveCall(call, outercc) and
|
callable = resolveCall(call, outercc) and
|
||||||
parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa),
|
parameterCand(callable, pragma[only_bind_into](i), pragma[only_bind_into](apa),
|
||||||
pragma[only_bind_into](config))
|
pragma[only_bind_into](config))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3687,18 +3606,14 @@ private predicate pathIntoCallable(
|
|||||||
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
PathNodeMid mid, ParamNodeEx p, CallContext outercc, CallContextCall innercc, SummaryCtx sc,
|
||||||
DataFlowCall call, Configuration config
|
DataFlowCall call, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParameterPosition pos, DataFlowCallable callable, AccessPath ap |
|
exists(int i, DataFlowCallable callable, AccessPath ap |
|
||||||
pathIntoCallable0(mid, callable, pos, outercc, call, ap, config) and
|
pathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||||
p.isParameterOf(callable, pos) and
|
p.isParameterOf(callable, i) and
|
||||||
(
|
(
|
||||||
sc = TSummaryCtxSome(p, ap)
|
sc = TSummaryCtxSome(p, ap)
|
||||||
or
|
or
|
||||||
not exists(TSummaryCtxSome(p, ap)) and
|
not exists(TSummaryCtxSome(p, ap)) and
|
||||||
sc = TSummaryCtxNone() and
|
sc = TSummaryCtxNone()
|
||||||
// When the call contexts of source and sink needs to match then there's
|
|
||||||
// never any reason to enter a callable except to find a summary. See also
|
|
||||||
// the comment in `PathNodeMid::isAtSink`.
|
|
||||||
not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext
|
|
||||||
)
|
)
|
||||||
|
|
|
|
||||||
if recordDataFlowCallSite(call, callable)
|
if recordDataFlowCallSite(call, callable)
|
||||||
@@ -3713,7 +3628,7 @@ private predicate paramFlowsThrough(
|
|||||||
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
|
ReturnKindExt kind, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, AccessPathApprox apa,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos |
|
exists(PathNodeMid mid, RetNodeEx ret, int pos |
|
||||||
mid.getNodeEx() = ret and
|
mid.getNodeEx() = ret and
|
||||||
kind = ret.getKind() and
|
kind = ret.getKind() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
@@ -3761,14 +3676,13 @@ private module Subpaths {
|
|||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate subpaths01(
|
private predicate subpaths01(
|
||||||
PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind,
|
||||||
NodeEx out, AccessPath apout
|
NodeEx out, AccessPath apout
|
||||||
) {
|
) {
|
||||||
exists(Configuration config |
|
exists(Configuration config |
|
||||||
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
pathThroughCallable(arg, out, _, pragma[only_bind_into](apout)) and
|
||||||
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
pathIntoCallable(arg, par, _, innercc, sc, _, config) and
|
||||||
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config)) and
|
paramFlowsThrough(kind, innercc, sc, pragma[only_bind_into](apout), _, unbindConf(config))
|
||||||
not arg.isHidden()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3802,17 +3716,8 @@ private module Subpaths {
|
|||||||
innercc = ret.getCallContext() and
|
innercc = ret.getCallContext() and
|
||||||
sc = ret.getSummaryCtx() and
|
sc = ret.getSummaryCtx() and
|
||||||
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
ret.getConfiguration() = unbindConf(getPathNodeConf(arg)) and
|
||||||
apout = ret.getAp()
|
apout = ret.getAp() and
|
||||||
)
|
not ret.isHidden()
|
||||||
}
|
|
||||||
|
|
||||||
private PathNodeImpl localStepToHidden(PathNodeImpl n) {
|
|
||||||
n.getASuccessorImpl() = result and
|
|
||||||
result.isHidden() and
|
|
||||||
exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() |
|
|
||||||
localFlowBigStep(n1, n2, _, _, _, _) or
|
|
||||||
store(n1, _, n2, _, _) or
|
|
||||||
read(n1, _, n2, _)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3821,12 +3726,11 @@ private module Subpaths {
|
|||||||
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
* a subpath between `par` and `ret` with the connecting edges `arg -> par` and
|
||||||
* `ret -> out` is summarized as the edge `arg -> out`.
|
* `ret -> out` is summarized as the edge `arg -> out`.
|
||||||
*/
|
*/
|
||||||
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeImpl ret, PathNodeMid out) {
|
predicate subpaths(PathNode arg, PathNodeImpl par, PathNodeMid ret, PathNodeMid out) {
|
||||||
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
exists(ParamNodeEx p, NodeEx o, AccessPath apout |
|
||||||
pragma[only_bind_into](arg).getASuccessor() = par and
|
pragma[only_bind_into](arg).getASuccessor() = par and
|
||||||
pragma[only_bind_into](arg).getASuccessor() = out and
|
pragma[only_bind_into](arg).getASuccessor() = out and
|
||||||
subpaths03(arg, p, localStepToHidden*(ret), o, apout) and
|
subpaths03(arg, p, ret, o, apout) and
|
||||||
not ret.isHidden() and
|
|
||||||
par.getNodeEx() = p and
|
par.getNodeEx() = p and
|
||||||
out.getNodeEx() = o and
|
out.getNodeEx() = o and
|
||||||
out.getAp() = apout
|
out.getAp() = apout
|
||||||
@@ -4442,25 +4346,24 @@ private module FlowExploration {
|
|||||||
|
|
||||||
pragma[noinline]
|
pragma[noinline]
|
||||||
private predicate partialPathIntoArg(
|
private predicate partialPathIntoArg(
|
||||||
PartialPathNodeFwd mid, ParameterPosition ppos, CallContext cc, DataFlowCall call,
|
PartialPathNodeFwd mid, int i, CallContext cc, DataFlowCall call, PartialAccessPath ap,
|
||||||
PartialAccessPath ap, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ArgNode arg, ArgumentPosition apos |
|
exists(ArgNode arg |
|
||||||
arg = mid.getNodeEx().asNode() and
|
arg = mid.getNodeEx().asNode() and
|
||||||
cc = mid.getCallContext() and
|
cc = mid.getCallContext() and
|
||||||
arg.argumentOf(call, apos) and
|
arg.argumentOf(call, i) and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate partialPathIntoCallable0(
|
private predicate partialPathIntoCallable0(
|
||||||
PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, CallContext outercc,
|
PartialPathNodeFwd mid, DataFlowCallable callable, int i, CallContext outercc,
|
||||||
DataFlowCall call, PartialAccessPath ap, Configuration config
|
DataFlowCall call, PartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
partialPathIntoArg(mid, pos, outercc, call, ap, config) and
|
partialPathIntoArg(mid, i, outercc, call, ap, config) and
|
||||||
callable = resolveCall(call, outercc)
|
callable = resolveCall(call, outercc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4469,9 +4372,9 @@ private module FlowExploration {
|
|||||||
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
TSummaryCtx1 sc1, TSummaryCtx2 sc2, DataFlowCall call, PartialAccessPath ap,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(ParameterPosition pos, DataFlowCallable callable |
|
exists(int i, DataFlowCallable callable |
|
||||||
partialPathIntoCallable0(mid, callable, pos, outercc, call, ap, config) and
|
partialPathIntoCallable0(mid, callable, i, outercc, call, ap, config) and
|
||||||
p.isParameterOf(callable, pos) and
|
p.isParameterOf(callable, i) and
|
||||||
sc1 = TSummaryCtx1Param(p) and
|
sc1 = TSummaryCtx1Param(p) and
|
||||||
sc2 = TSummaryCtx2Some(ap)
|
sc2 = TSummaryCtx2Some(ap)
|
||||||
|
|
|
|
||||||
@@ -4635,23 +4538,22 @@ private module FlowExploration {
|
|||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate revPartialPathFlowsThrough(
|
private predicate revPartialPathFlowsThrough(
|
||||||
ArgumentPosition apos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2,
|
int pos, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, RevPartialAccessPath ap,
|
||||||
RevPartialAccessPath ap, Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos |
|
exists(PartialPathNodeRev mid, ParamNodeEx p |
|
||||||
mid.getNodeEx() = p and
|
mid.getNodeEx() = p and
|
||||||
p.getPosition() = ppos and
|
p.getPosition() = pos and
|
||||||
sc1 = mid.getSummaryCtx1() and
|
sc1 = mid.getSummaryCtx1() and
|
||||||
sc2 = mid.getSummaryCtx2() and
|
sc2 = mid.getSummaryCtx2() and
|
||||||
ap = mid.getAp() and
|
ap = mid.getAp() and
|
||||||
config = mid.getConfiguration() and
|
config = mid.getConfiguration()
|
||||||
parameterMatch(ppos, apos)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
private predicate revPartialPathThroughCallable0(
|
private predicate revPartialPathThroughCallable0(
|
||||||
DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, RevPartialAccessPath ap,
|
DataFlowCall call, PartialPathNodeRev mid, int pos, RevPartialAccessPath ap,
|
||||||
Configuration config
|
Configuration config
|
||||||
) {
|
) {
|
||||||
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2 |
|
||||||
@@ -4664,7 +4566,7 @@ private module FlowExploration {
|
|||||||
private predicate revPartialPathThroughCallable(
|
private predicate revPartialPathThroughCallable(
|
||||||
PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
|
PartialPathNodeRev mid, ArgNodeEx node, RevPartialAccessPath ap, Configuration config
|
||||||
) {
|
) {
|
||||||
exists(DataFlowCall call, ArgumentPosition pos |
|
exists(DataFlowCall call, int pos |
|
||||||
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
revPartialPathThroughCallable0(call, mid, pos, ap, config) and
|
||||||
node.asNode().(ArgNode).argumentOf(call, pos)
|
node.asNode().(ArgNode).argumentOf(call, pos)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,20 +2,6 @@ private import cpp
|
|||||||
private import DataFlowUtil
|
private import DataFlowUtil
|
||||||
private import DataFlowDispatch
|
private import DataFlowDispatch
|
||||||
private import FlowVar
|
private import FlowVar
|
||||||
private import DataFlowImplConsistency
|
|
||||||
|
|
||||||
/** Gets the callable in which this node occurs. */
|
|
||||||
DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() }
|
|
||||||
|
|
||||||
/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
|
|
||||||
predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) {
|
|
||||||
p.isParameterOf(c, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */
|
|
||||||
predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos) {
|
|
||||||
arg.argumentOf(c, pos)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Gets the instance argument of a non-static call. */
|
/** Gets the instance argument of a non-static call. */
|
||||||
private Node getInstanceArgument(Call call) {
|
private Node getInstanceArgument(Call call) {
|
||||||
@@ -267,6 +253,27 @@ class Unit extends TUnit {
|
|||||||
string toString() { result = "unit" }
|
string toString() { result = "unit" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `n` does not require a `PostUpdateNode` as it either cannot be
|
||||||
|
* modified or its modification cannot be observed, for example if it is a
|
||||||
|
* freshly created object that is not saved in a variable.
|
||||||
|
*
|
||||||
|
* This predicate is only used for consistency checks.
|
||||||
|
*/
|
||||||
|
predicate isImmutableOrUnobservable(Node n) {
|
||||||
|
// Is the null pointer (or something that's not really a pointer)
|
||||||
|
exists(n.asExpr().getValue())
|
||||||
|
or
|
||||||
|
// Isn't a pointer or is a pointer to const
|
||||||
|
forall(DerivedType dt | dt = n.asExpr().getActualType() |
|
||||||
|
dt.getBaseType().isConst()
|
||||||
|
or
|
||||||
|
dt.getBaseType() instanceof RoutineType
|
||||||
|
)
|
||||||
|
// The above list of cases isn't exhaustive, but it narrows down the
|
||||||
|
// consistency alerts enough that most of them are interesting.
|
||||||
|
}
|
||||||
|
|
||||||
/** Holds if `n` should be hidden from path explanations. */
|
/** Holds if `n` should be hidden from path explanations. */
|
||||||
predicate nodeIsHidden(Node n) { none() }
|
predicate nodeIsHidden(Node n) { none() }
|
||||||
|
|
||||||
@@ -289,19 +296,3 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves
|
|||||||
* by default as a heuristic.
|
* by default as a heuristic.
|
||||||
*/
|
*/
|
||||||
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
predicate allowParameterReturnInSelf(ParameterNode p) { none() }
|
||||||
|
|
||||||
private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration {
|
|
||||||
override predicate argHasPostUpdateExclude(ArgumentNode n) {
|
|
||||||
// Is the null pointer (or something that's not really a pointer)
|
|
||||||
exists(n.asExpr().getValue())
|
|
||||||
or
|
|
||||||
// Isn't a pointer or is a pointer to const
|
|
||||||
forall(DerivedType dt | dt = n.asExpr().getActualType() |
|
|
||||||
dt.getBaseType().isConst()
|
|
||||||
or
|
|
||||||
dt.getBaseType() instanceof RoutineType
|
|
||||||
)
|
|
||||||
// The above list of cases isn't exhaustive, but it narrows down the
|
|
||||||
// consistency alerts enough that most of them are interesting.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class Expr extends StmtParent, @expr {
|
|||||||
override Stmt getEnclosingStmt() {
|
override Stmt getEnclosingStmt() {
|
||||||
result = this.getParent().(Expr).getEnclosingStmt()
|
result = this.getParent().(Expr).getEnclosingStmt()
|
||||||
or
|
or
|
||||||
result = this.getParent()
|
result = this.getParent().(Stmt)
|
||||||
or
|
or
|
||||||
exists(Expr other | result = other.getEnclosingStmt() and other.getConversion() = this)
|
exists(Expr other | result = other.getEnclosingStmt() and other.getConversion() = this)
|
||||||
or
|
or
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class LambdaCapture extends Locatable, @lambdacapture {
|
|||||||
* An identifier is captured by reference if:
|
* An identifier is captured by reference if:
|
||||||
* - It is explicitly captured by reference.
|
* - It is explicitly captured by reference.
|
||||||
* - It is implicitly captured, and the lambda's default capture mode is by-reference.
|
* - It is implicitly captured, and the lambda's default capture mode is by-reference.
|
||||||
* - The identifier is "this". [Said behavior is dictated by the C++11 standard, but it
|
* - The identifier is "this". [Said behaviour is dictated by the C++11 standard, but it
|
||||||
* is actually "*this" being captured rather than "this".]
|
* is actually "*this" being captured rather than "this".]
|
||||||
*/
|
*/
|
||||||
predicate isCapturedByReference() { lambda_capture(this, _, _, _, true, _, _) }
|
predicate isCapturedByReference() { lambda_capture(this, _, _, _, true, _, _) }
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user